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">
<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 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>
</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 { 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 (
<>
<div
@ -229,6 +238,11 @@ export function ChatMessage({
<button className="message-zap-button" onClick={pickEmoji}>
<Icon name="face" className="message-zap-button-icon" />
</button>
{shouldShowMuteButton && (
<button className="message-zap-button" onClick={muteUser}>
<Icon name="user-x" className="message-zap-button-icon" />
</button>
)}
</div>
)}
</div>

View File

@ -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 (
<AsyncButton
type="button"

View File

@ -42,17 +42,19 @@ export function useStreamsFeed(tag?: string) {
return [];
}, [feed.data]);
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);
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 };
}

View File

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