mirror of
https://github.com/lumehq/lume.git
synced 2025-03-26 01:31:48 +01:00
update channel
This commit is contained in:
parent
50b4914979
commit
4be369b671
@ -1,7 +1,7 @@
|
||||
import MutedItem from '@lume/app/channel/components/mutedItem';
|
||||
import MuteIcon from '@lume/shared/icons/mute';
|
||||
|
||||
import { Popover, Transition } from '@headlessui/react';
|
||||
import { MicMute } from 'iconoir-react';
|
||||
import { Fragment } from 'react';
|
||||
|
||||
export default function ChannelBlackList({ blacklist }: { blacklist: any }) {
|
||||
@ -14,7 +14,7 @@ export default function ChannelBlackList({ blacklist }: { blacklist: any }) {
|
||||
open ? 'bg-zinc-800 hover:bg-zinc-700' : 'bg-zinc-900 hover:bg-zinc-800'
|
||||
}`}
|
||||
>
|
||||
<MicMute width={16} height={16} className="text-zinc-400 group-hover:text-zinc-200" />
|
||||
<MuteIcon width={16} height={16} className="text-zinc-400 group-hover:text-zinc-200" />
|
||||
</Popover.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
|
@ -1,17 +1,32 @@
|
||||
import CancelIcon from '@lume/shared/icons/cancel';
|
||||
import HideIcon from '@lume/shared/icons/hide';
|
||||
import { RelayContext } from '@lume/shared/relayProvider';
|
||||
import Tooltip from '@lume/shared/tooltip';
|
||||
import { channelMessagesAtom } from '@lume/stores/channel';
|
||||
import { WRITEONLY_RELAYS } from '@lume/stores/constants';
|
||||
import { dateToUnix } from '@lume/utils/getDate';
|
||||
import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount';
|
||||
|
||||
import { EyeClose } from 'iconoir-react';
|
||||
import { Dialog, Transition } from '@headlessui/react';
|
||||
import { useAtom } from 'jotai';
|
||||
import { getEventHash, signEvent } from 'nostr-tools';
|
||||
import { useContext } from 'react';
|
||||
import { Fragment, useContext, useState } from 'react';
|
||||
|
||||
export default function MessageHideButton({ id }: { id: string }) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const { account, isError, isLoading } = useActiveAccount();
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [messages, setMessages] = useAtom(channelMessagesAtom);
|
||||
|
||||
const closeModal = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const openModal = () => {
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const hideMessage = () => {
|
||||
if (!isError && !isLoading && account) {
|
||||
const event: any = {
|
||||
@ -26,19 +41,98 @@ export default function MessageHideButton({ id }: { id: string }) {
|
||||
|
||||
// publish note
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
// update local state
|
||||
const cloneMessages = [...messages];
|
||||
const targetMessage = cloneMessages.findIndex((message) => message.id === id);
|
||||
cloneMessages[targetMessage]['hide'] = true;
|
||||
setMessages(cloneMessages);
|
||||
// close modal
|
||||
closeModal();
|
||||
} else {
|
||||
console.log('error');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip message="Hide this message">
|
||||
<button
|
||||
onClick={() => hideMessage()}
|
||||
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" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<>
|
||||
<Tooltip message="Hide this message">
|
||||
<button
|
||||
onClick={openModal}
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-800"
|
||||
>
|
||||
<HideIcon width={16} height={16} className="text-zinc-200" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
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 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="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<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"
|
||||
>
|
||||
Are you sure!
|
||||
</Dialog.Title>
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
autoFocus={false}
|
||||
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="leading-tight text-zinc-400">
|
||||
This message will be hidden from your feed.
|
||||
</Dialog.Description>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
className="inline-flex h-9 items-center justify-center rounded-md px-2 text-sm font-medium text-zinc-400 hover:bg-zinc-800 hover:text-zinc-200"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => hideMessage()}
|
||||
className="inline-flex h-9 items-center justify-center rounded-md bg-red-500 px-2 text-sm font-medium text-zinc-100 hover:bg-red-600"
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export default function ChannelMessageItem({ data }: { data: any }) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute -top-4 right-4 z-10 hidden group-hover:inline-flex">
|
||||
<div className="inline-flex h-7 items-center justify-center gap-1 rounded bg-zinc-900 px-0.5 shadow-md shadow-black/20 ring-1 ring-zinc-800">
|
||||
<div className="inline-flex h-8 items-center justify-center gap-1.5 rounded bg-zinc-900 px-0.5 shadow-md shadow-black/20 ring-1 ring-zinc-800">
|
||||
<MessageReplyButton id={data.id} pubkey={data.pubkey} content={data.content} />
|
||||
<MessageHideButton id={data.id} />
|
||||
<MessageMuteButton pubkey={data.pubkey} />
|
||||
|
@ -1,17 +1,32 @@
|
||||
import CancelIcon from '@lume/shared/icons/cancel';
|
||||
import MuteIcon from '@lume/shared/icons/mute';
|
||||
import { RelayContext } from '@lume/shared/relayProvider';
|
||||
import Tooltip from '@lume/shared/tooltip';
|
||||
import { channelMessagesAtom } from '@lume/stores/channel';
|
||||
import { WRITEONLY_RELAYS } from '@lume/stores/constants';
|
||||
import { dateToUnix } from '@lume/utils/getDate';
|
||||
import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount';
|
||||
|
||||
import { MicMute } from 'iconoir-react';
|
||||
import { Dialog, Transition } from '@headlessui/react';
|
||||
import { useAtom } from 'jotai';
|
||||
import { getEventHash, signEvent } from 'nostr-tools';
|
||||
import { useContext } from 'react';
|
||||
import { Fragment, useContext, useState } from 'react';
|
||||
|
||||
export default function MessageMuteButton({ pubkey }: { pubkey: string }) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const { account, isError, isLoading } = useActiveAccount();
|
||||
|
||||
const [messages, setMessages] = useAtom(channelMessagesAtom);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const closeModal = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const openModal = () => {
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const muteUser = () => {
|
||||
if (!isError && !isLoading && account) {
|
||||
const event: any = {
|
||||
@ -26,19 +41,97 @@ export default function MessageMuteButton({ pubkey }: { pubkey: string }) {
|
||||
|
||||
// publish note
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
// update local state
|
||||
const cloneMessages = [...messages];
|
||||
const finalMessages = cloneMessages.filter((message) => message.pubkey !== pubkey);
|
||||
setMessages(finalMessages);
|
||||
// close modal
|
||||
closeModal();
|
||||
} else {
|
||||
console.log('error');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip message="Mute this user">
|
||||
<button
|
||||
onClick={() => muteUser()}
|
||||
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" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<>
|
||||
<Tooltip message="Mute this user">
|
||||
<button
|
||||
onClick={() => openModal()}
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-800"
|
||||
>
|
||||
<MuteIcon width={16} height={16} className="text-zinc-200" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
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 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="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<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"
|
||||
>
|
||||
Are you sure!
|
||||
</Dialog.Title>
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
autoFocus={false}
|
||||
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="leading-tight text-zinc-400">
|
||||
You will no longer see messages from this user.
|
||||
</Dialog.Description>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
className="inline-flex h-9 items-center justify-center rounded-md px-2 text-sm font-medium text-zinc-400 hover:bg-zinc-800 hover:text-zinc-200"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => muteUser()}
|
||||
className="inline-flex h-9 items-center justify-center rounded-md bg-red-500 px-2 text-sm font-medium text-zinc-100 hover:bg-red-600"
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import ReplyMessageIcon from '@lume/shared/icons/replyMessage';
|
||||
import Tooltip from '@lume/shared/tooltip';
|
||||
import { channelReplyAtom } from '@lume/stores/channel';
|
||||
|
||||
import { Reply } from 'iconoir-react';
|
||||
import { useSetAtom } from 'jotai';
|
||||
|
||||
export default function MessageReplyButton({ id, pubkey, content }: { id: string; pubkey: string; content: string }) {
|
||||
@ -12,12 +12,12 @@ export default function MessageReplyButton({ id, pubkey, content }: { id: string
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip message="Reply">
|
||||
<Tooltip message="Reply to message">
|
||||
<button
|
||||
onClick={() => createReply()}
|
||||
className="inline-flex h-6 w-6 items-center justify-center rounded hover:bg-zinc-800"
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-800"
|
||||
>
|
||||
<Reply width={16} height={16} className="text-zinc-400" />
|
||||
<ReplyMessageIcon width={16} height={16} className="text-zinc-200" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
|
@ -64,6 +64,8 @@ export function Page() {
|
||||
const message: any = event;
|
||||
if (hided.includes(event.id)) {
|
||||
message['hide'] = true;
|
||||
} else {
|
||||
message['hide'] = false;
|
||||
}
|
||||
if (!muted.includes(event.pubkey)) {
|
||||
setChannelMessages((prev) => [...prev, message]);
|
||||
@ -77,10 +79,18 @@ export function Page() {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// reset channel reply
|
||||
resetChannelReply();
|
||||
// reset channel messages
|
||||
resetChannelMessages();
|
||||
let ignore = false;
|
||||
|
||||
if (!ignore) {
|
||||
// reset channel reply
|
||||
resetChannelReply();
|
||||
// reset channel messages
|
||||
resetChannelMessages();
|
||||
}
|
||||
|
||||
return () => {
|
||||
ignore = true;
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -52,8 +52,16 @@ export function Page() {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// reset channel messages
|
||||
resetChatMessages();
|
||||
let ignore = false;
|
||||
|
||||
if (!ignore) {
|
||||
// reset chat messages
|
||||
resetChatMessages();
|
||||
}
|
||||
|
||||
return () => {
|
||||
ignore = true;
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
|
12
src/shared/icons/hide.tsx
Normal file
12
src/shared/icons/hide.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { SVGProps } from 'react';
|
||||
|
||||
export default function HideIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width={24} height={24} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M9.1654 4.42071C8.76876 4.5401 8.544 4.95841 8.66339 5.35505C8.78277 5.75169 9.20109 5.97645 9.59772 5.85706L9.1654 4.42071ZM22 12L22.671 12.3351C22.7763 12.1241 22.7763 11.8759 22.671 11.6649L22 12ZM19.1413 14.9666C18.8678 15.2776 18.8982 15.7515 19.2092 16.0251C19.5203 16.2986 19.9942 16.2682 20.2677 15.9571L19.1413 14.9666ZM3.28033 2.21967C2.98744 1.92678 2.51256 1.92678 2.21967 2.21967C1.92678 2.51256 1.92678 2.98744 2.21967 3.28033L3.28033 2.21967ZM2 11.9999L1.32902 11.6648C1.22366 11.8758 1.22366 12.124 1.32902 12.335L2 11.9999ZM17.4703 17.4703L18.0006 16.9399L17.4703 17.4703ZM20.7197 21.7803C21.0126 22.0732 21.4874 22.0732 21.7803 21.7803C22.0732 21.4874 22.0732 21.0126 21.7803 20.7197L20.7197 21.7803ZM10.2322 10.2322C10.5251 9.93934 10.5251 9.46447 10.2322 9.17157C9.93934 8.87868 9.46447 8.87868 9.17157 9.17157L10.2322 10.2322ZM14.8284 14.8284C15.1213 14.5355 15.1213 14.0607 14.8284 13.7678C14.5355 13.4749 14.0607 13.4749 13.7678 13.7678L14.8284 14.8284ZM9.59772 5.85706C13.745 4.60878 18.4769 6.624 21.329 12.3351L22.671 11.6649C19.5775 5.47055 14.1791 2.91165 9.1654 4.42071L9.59772 5.85706ZM20.2677 15.9571C21.1654 14.9364 21.9755 13.7277 22.671 12.3351L21.329 11.6649C20.6865 12.9515 19.9468 14.0507 19.1413 14.9666L20.2677 15.9571ZM2.21967 3.28033L5.99937 7.06003L7.06003 5.99937L3.28033 2.21967L2.21967 3.28033ZM2.67098 12.335C3.84083 9.99245 5.33197 8.27257 6.95699 7.14609L6.10242 5.91332C4.24158 7.20327 2.5948 9.13019 1.32902 11.6648L2.67098 12.335ZM5.99937 7.06003L16.9399 18.0006L18.0006 16.9399L7.06003 5.99937L5.99937 7.06003ZM16.9399 18.0006L20.7197 21.7803L21.7803 20.7197L18.0006 16.9399L16.9399 18.0006ZM1.32902 12.335C3.20469 16.0909 5.92036 18.5148 8.91701 19.5009C11.922 20.4898 15.1308 20.0045 17.8975 18.0866L17.043 16.8539C14.6436 18.5171 11.9221 18.9107 9.38589 18.0761C6.84135 17.2388 4.40494 15.1369 2.67098 11.6648L1.32902 12.335ZM12 14.5C10.6193 14.5 9.5 13.3807 9.5 12H8C8 14.2091 9.79086 16 12 16V14.5ZM9.5 12C9.5 11.3094 9.779 10.6855 10.2322 10.2322L9.17157 9.17157C8.44854 9.89461 8 10.8956 8 12H9.5ZM13.7678 13.7678C13.3145 14.221 12.6906 14.5 12 14.5V16C13.1044 16 14.1054 15.5515 14.8284 14.8284L13.7678 13.7678Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
18
src/shared/icons/mute.tsx
Normal file
18
src/shared/icons/mute.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { SVGProps } from 'react';
|
||||
|
||||
export default function MuteIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width={24} height={24} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M17 5.93934V3.75C17 3.47592 16.8505 3.22366 16.6101 3.09208C16.3696 2.9605 16.0766 2.97055 15.8457 3.1183L9.78055 7H5.75C5.33579 7 5 7.33579 5 7.75V16.25C5 16.6642 5.33579 17 5.75 17H5.93934L3.21967 19.7197C2.92678 20.0126 2.92678 20.4874 3.21967 20.7803C3.51256 21.0732 3.98744 21.0732 4.28033 20.7803L20.7803 4.28033C21.0732 3.98744 21.0732 3.51256 20.7803 3.21967C20.4874 2.92678 20.0126 2.92678 19.7197 3.21967L17 5.93934ZM7.43934 15.5H6.5V8.5H10C10.1433 8.5 10.2836 8.45895 10.4043 8.3817L15.5 5.12045V7.43934L7.43934 15.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M15.5 18.8796L11.1102 16.0701L10.0243 17.156L15.8457 20.8817C16.0766 21.0294 16.3696 21.0395 16.6101 20.9079C16.8505 20.7763 17 20.5241 17 20.25V10.1803L15.5 11.6803V18.8796Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
14
src/shared/icons/replyMessage.tsx
Normal file
14
src/shared/icons/replyMessage.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { SVGProps } from 'react';
|
||||
|
||||
export default function ReplyMessageIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width={24} height={24} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M1.75 12L11.25 3.75V8.5C19.75 8.5 22 11.75 22 20.25C20.5 17.25 19.75 15.5 11.25 15.5V20.25L1.75 12Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user