Merge pull request 'feat: in-chat mute button' (#76) from in-chat-mute into main

Reviewed-on: Kieran/stream#76
Reviewed-by: Kieran <kieran@noreply.localhost>
This commit is contained in:
Kieran 2023-08-06 13:58:37 +00:00
commit 877be3d6b6
5 changed files with 46 additions and 17 deletions

View File

@ -85,5 +85,8 @@
<symbol id="face-content" viewBox="0 0 24 24" fill="none"> <symbol id="face-content" viewBox="0 0 24 24" fill="none">
<path d="M8 14C8 14 9.5 16 12 16C14.5 16 16 14 16 14M17 9.24C16.605 9.725 16.065 10 15.5 10C14.935 10 14.41 9.725 14 9.24M10 9.24C9.605 9.725 9.065 10 8.5 10C7.935 10 7.41 9.725 7 9.24M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M8 14C8 14 9.5 16 12 16C14.5 16 16 14 16 14M17 9.24C16.605 9.725 16.065 10 15.5 10C14.935 10 14.41 9.725 14 9.24M10 9.24C9.605 9.725 9.065 10 8.5 10C7.935 10 7.41 9.725 7 9.24M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol> </symbol>
<symbol id="user-x" viewBox="0 0 24 24" fill="none">
<path d="M16.5 16L21.5 21M21.5 16L16.5 21M15.5 3.29076C16.9659 3.88415 18 5.32131 18 7C18 8.67869 16.9659 10.1159 15.5 10.7092M12 15H8C6.13623 15 5.20435 15 4.46927 15.3045C3.48915 15.7105 2.71046 16.4892 2.30448 17.4693C2 18.2044 2 19.1362 2 21M13.5 7C13.5 9.20914 11.7091 11 9.5 11C7.29086 11 5.5 9.20914 5.5 7C5.5 4.79086 7.29086 3 9.5 3C11.7091 3 13.5 4.79086 13.5 7Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
</defs> </defs>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -13,6 +13,7 @@ import { Icon } from "element/icon";
import { Emoji as EmojiComponent } from "element/emoji"; import { Emoji as EmojiComponent } from "element/emoji";
import { Profile } from "./profile"; import { Profile } from "./profile";
import { Text } from "element/text"; import { Text } from "element/text";
import { useMute } from "element/mute-button";
import { SendZapsDialog } from "element/send-zap"; import { SendZapsDialog } from "element/send-zap";
import { CollapsibleEvent } from "element/collapsible"; import { CollapsibleEvent } from "element/collapsible";
import { useLogin } from "hooks/login"; import { useLogin } from "hooks/login";
@ -55,6 +56,7 @@ export function ChatMessage({
const emojiRef = useRef(null); const emojiRef = useRef(null);
const isTablet = useMediaQuery("(max-width: 1020px)"); const isTablet = useMediaQuery("(max-width: 1020px)");
const isHovering = useHover(ref); const isHovering = useHover(ref);
const { mute } = useMute(ev.pubkey);
const [showZapDialog, setShowZapDialog] = useState(false); const [showZapDialog, setShowZapDialog] = useState(false);
const [showEmojiPicker, setShowEmojiPicker] = useState(false); const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const login = useLogin(); const login = useLogin();
@ -62,6 +64,8 @@ export function ChatMessage({
System, System,
inView?.isIntersecting ? ev.pubkey : undefined inView?.isIntersecting ? ev.pubkey : undefined
); );
const shouldShowMuteButton =
ev.pubkey !== streamer && ev.pubkey != login?.pubkey;
const zapTarget = profile?.lud16 ?? profile?.lud06; const zapTarget = profile?.lud16 ?? profile?.lud06;
const zaps = useMemo(() => { const zaps = useMemo(() => {
return reactions return reactions
@ -132,11 +136,16 @@ export function ChatMessage({
const topOffset = ref.current?.getBoundingClientRect().top; const topOffset = ref.current?.getBoundingClientRect().top;
const leftOffset = ref.current?.getBoundingClientRect().left; const leftOffset = ref.current?.getBoundingClientRect().left;
function pickEmoji(ev: React.MouseEvent) { function pickEmoji(e: React.MouseEvent) {
ev.stopPropagation(); e.stopPropagation();
setShowEmojiPicker(!showEmojiPicker); setShowEmojiPicker(!showEmojiPicker);
} }
async function muteUser(e: React.MouseEvent) {
e.stopPropagation();
mute();
}
return ( return (
<> <>
<div <div
@ -229,6 +238,11 @@ export function ChatMessage({
<button className="message-zap-button" onClick={pickEmoji}> <button className="message-zap-button" onClick={pickEmoji}>
<Icon name="face" className="message-zap-button-icon" /> <Icon name="face" className="message-zap-button-icon" />
</button> </button>
{shouldShowMuteButton && (
<button className="message-zap-button" onClick={muteUser}>
<Icon name="user-x" className="message-zap-button-icon" />
</button>
)}
</div> </div>
)} )}
</div> </div>

View File

@ -1,13 +1,17 @@
import { useMemo } from "react";
import { useLogin } from "hooks/login"; import { useLogin } from "hooks/login";
import AsyncButton from "element/async-button"; import AsyncButton from "element/async-button";
import { Login, System } from "index"; import { Login, System } from "index";
import { MUTED } from "const"; import { MUTED } from "const";
export function LoggedInMuteButton({ pubkey }: { pubkey: string }) { export function useMute(pubkey: string) {
const login = useLogin(); const login = useLogin();
const { tags, content } = login!.muted; const { tags, content } = login!.muted;
const muted = tags.filter((t) => t.at(0) === "p"); const muted = useMemo(() => tags.filter((t) => t.at(0) === "p"), [tags]);
const isMuted = muted.find((t) => t.at(1) === pubkey); const isMuted = useMemo(
() => muted.find((t) => t.at(1) === pubkey),
[pubkey, muted]
);
async function unmute() { async function unmute() {
const pub = login?.publisher(); 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 ( return (
<AsyncButton <AsyncButton
type="button" type="button"

View File

@ -42,17 +42,19 @@ export function useStreamsFeed(tag?: string) {
return []; return [];
}, [feed.data]); }, [feed.data]);
const live = feedSorted.filter( const live = feedSorted
(a) => findTag(a, "status") === StreamState.Live .filter((a) => findTag(a, "status") === StreamState.Live)
).sort(sortStarts); .sort(sortStarts);
const planned = feedSorted.filter( const planned = feedSorted
(a) => findTag(a, "status") === StreamState.Planned .filter((a) => findTag(a, "status") === StreamState.Planned)
).sort(sortStarts); .sort(sortStarts);
const ended = feedSorted.filter((a) => { const ended = feedSorted
const hasEnded = findTag(a, "status") === StreamState.Ended; .filter((a) => {
const recording = findTag(a, "recording") ?? ""; const hasEnded = findTag(a, "status") === StreamState.Ended;
return hasEnded && recording?.length > 0; const recording = findTag(a, "recording") ?? "";
}).sort(sortCreatedAt); return hasEnded && recording?.length > 0;
})
.sort(sortCreatedAt);
return { live, planned, ended }; return { live, planned, ended };
} }

View File

@ -10,7 +10,7 @@ export enum LoginType {
} }
interface ReplaceableTags { interface ReplaceableTags {
tags: Array<string[]>; tags: Tags;
content?: string; content?: string;
timestamp: number; timestamp: number;
} }