From 78d9a2779ae7677981ee7beeda633df8d09e4764 Mon Sep 17 00:00:00 2001 From: verbiricha Date: Fri, 4 Aug 2023 18:28:08 +0200 Subject: [PATCH] feat: in-chat mute button resolves #73 --- public/icons.svg | 3 +++ src/element/chat-message.tsx | 18 ++++++++++++++++-- src/element/mute-button.tsx | 16 +++++++++++++--- src/hooks/live-streams.ts | 24 +++++++++++++----------- src/login.ts | 2 +- 5 files changed, 46 insertions(+), 17 deletions(-) diff --git a/public/icons.svg b/public/icons.svg index 8de597e..a7420d8 100644 --- a/public/icons.svg +++ b/public/icons.svg @@ -85,5 +85,8 @@ + + + diff --git a/src/element/chat-message.tsx b/src/element/chat-message.tsx index 858d16d..f472c23 100644 --- a/src/element/chat-message.tsx +++ b/src/element/chat-message.tsx @@ -13,6 +13,7 @@ import { Icon } from "element/icon"; import { Emoji as EmojiComponent } from "element/emoji"; import { Profile } from "./profile"; import { Text } from "element/text"; +import { useMute } from "element/mute-button"; import { SendZapsDialog } from "element/send-zap"; import { CollapsibleEvent } from "element/collapsible"; import { useLogin } from "hooks/login"; @@ -55,6 +56,7 @@ export function ChatMessage({ const emojiRef = useRef(null); const isTablet = useMediaQuery("(max-width: 1020px)"); const isHovering = useHover(ref); + const { mute } = useMute(ev.pubkey); const [showZapDialog, setShowZapDialog] = useState(false); const [showEmojiPicker, setShowEmojiPicker] = useState(false); const login = useLogin(); @@ -62,6 +64,8 @@ export function ChatMessage({ System, inView?.isIntersecting ? ev.pubkey : undefined ); + const shouldShowMuteButton = + ev.pubkey !== streamer && ev.pubkey != login?.pubkey; const zapTarget = profile?.lud16 ?? profile?.lud06; const zaps = useMemo(() => { return reactions @@ -132,11 +136,16 @@ export function ChatMessage({ const topOffset = ref.current?.getBoundingClientRect().top; const leftOffset = ref.current?.getBoundingClientRect().left; - function pickEmoji(ev: React.MouseEvent) { - ev.stopPropagation(); + function pickEmoji(e: React.MouseEvent) { + e.stopPropagation(); setShowEmojiPicker(!showEmojiPicker); } + async function muteUser(e: React.MouseEvent) { + e.stopPropagation(); + mute(); + } + return ( <>
+ {shouldShowMuteButton && ( + + )}
)} diff --git a/src/element/mute-button.tsx b/src/element/mute-button.tsx index b008813..a992469 100644 --- a/src/element/mute-button.tsx +++ b/src/element/mute-button.tsx @@ -1,13 +1,17 @@ +import { useMemo } from "react"; import { useLogin } from "hooks/login"; import AsyncButton from "element/async-button"; import { Login, System } from "index"; import { MUTED } from "const"; -export function LoggedInMuteButton({ pubkey }: { pubkey: string }) { +export function useMute(pubkey: string) { const login = useLogin(); const { tags, content } = login!.muted; - const muted = tags.filter((t) => t.at(0) === "p"); - const isMuted = muted.find((t) => t.at(1) === pubkey); + const muted = useMemo(() => tags.filter((t) => t.at(0) === "p"), [tags]); + const isMuted = useMemo( + () => muted.find((t) => t.at(1) === pubkey), + [pubkey, muted] + ); async function unmute() { const pub = login?.publisher(); @@ -43,6 +47,12 @@ export function LoggedInMuteButton({ pubkey }: { pubkey: string }) { } } + return { isMuted, mute, unmute }; +} + +export function LoggedInMuteButton({ pubkey }: { pubkey: string }) { + const { isMuted, mute, unmute } = useMute(pubkey); + return ( findTag(a, "status") === StreamState.Live - ).sort(sortStarts); - const planned = feedSorted.filter( - (a) => findTag(a, "status") === StreamState.Planned - ).sort(sortStarts); - const ended = feedSorted.filter((a) => { - const hasEnded = findTag(a, "status") === StreamState.Ended; - const recording = findTag(a, "recording") ?? ""; - return hasEnded && recording?.length > 0; - }).sort(sortCreatedAt); + const live = feedSorted + .filter((a) => findTag(a, "status") === StreamState.Live) + .sort(sortStarts); + const planned = feedSorted + .filter((a) => findTag(a, "status") === StreamState.Planned) + .sort(sortStarts); + const ended = feedSorted + .filter((a) => { + const hasEnded = findTag(a, "status") === StreamState.Ended; + const recording = findTag(a, "recording") ?? ""; + return hasEnded && recording?.length > 0; + }) + .sort(sortCreatedAt); return { live, planned, ended }; } diff --git a/src/login.ts b/src/login.ts index d06ecb0..dbb5b7a 100644 --- a/src/login.ts +++ b/src/login.ts @@ -10,7 +10,7 @@ export enum LoginType { } interface ReplaceableTags { - tags: Array; + tags: Tags; content?: string; timestamp: number; }