open reactions modal from note footer zappers list
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Martti Malmi 2024-01-12 14:00:40 +02:00
parent 26e12d1c0b
commit 7ee210da16
7 changed files with 37 additions and 26 deletions

View File

@ -20,9 +20,10 @@ import { useWallet } from "@/Wallet";
export interface ZapIconProps {
ev: TaggedNostrEvent;
zaps: Array<ParsedZap>;
onClickZappers?: () => void;
}
export const FooterZapButton = ({ ev, zaps }: ZapIconProps) => {
export const FooterZapButton = ({ ev, zaps, onClickZappers }: ZapIconProps) => {
const {
publicKey,
readonly,
@ -142,7 +143,7 @@ export const FooterZapButton = ({ ev, zaps }: ZapIconProps) => {
value={zapTotal}
onClick={fastZap}
/>
<ZapsSummary zaps={zaps} />
<ZapsSummary zaps={zaps} onClick={onClickZappers} />
</div>
{showZapModal && (
<ZapModal

View File

@ -1,12 +1,13 @@
import { NostrLink, TaggedNostrEvent } from "@snort/system";
import { useEventReactions, useReactions } from "@snort/system-react";
import { useMemo } from "react";
import React, { useMemo, useState } from "react";
import { FooterZapButton } from "@/Components/Event/Note/NoteFooter/FooterZapButton";
import { LikeButton } from "@/Components/Event/Note/NoteFooter/LikeButton";
import { PowIcon } from "@/Components/Event/Note/NoteFooter/PowIcon";
import { ReplyButton } from "@/Components/Event/Note/NoteFooter/ReplyButton";
import { RepostButton } from "@/Components/Event/Note/NoteFooter/RepostButton";
import ReactionsModal from "@/Components/Event/Note/ReactionsModal";
import useLogin from "@/Hooks/useLogin";
export interface NoteFooterProps {
@ -18,6 +19,7 @@ export default function NoteFooter(props: NoteFooterProps) {
const { ev } = props;
const link = useMemo(() => NostrLink.fromEvent(ev), [ev.id]);
const ids = useMemo(() => [link], [link]);
const [showReactions, setShowReactions] = useState(false);
const related = useReactions("note:reactions", ids, undefined, false);
const { reactions, zaps, reposts } = useEventReactions(link, related);
@ -35,7 +37,8 @@ export default function NoteFooter(props: NoteFooterProps) {
{!readonly && <RepostButton ev={ev} reposts={reposts} />}
{prefs.enableReactions && <LikeButton ev={ev} positiveReactions={positive} />}
{CONFIG.showPowIcon && <PowIcon ev={ev} />}
<FooterZapButton ev={ev} zaps={zaps} />
<FooterZapButton ev={ev} zaps={zaps} onClickZappers={() => setShowReactions(true)} />
{showReactions && <ReactionsModal onClose={() => setShowReactions(false)} event={ev} />}
</div>
);
}

View File

@ -84,7 +84,7 @@ export default function NoteHeader(props: {
/>
)}
</div>
<ReactionsModal show={showReactions} setShow={setShowReactions} event={ev} />
{showReactions && <ReactionsModal onClose={() => setShowReactions(false)} event={ev} />}
</div>
);
}

View File

@ -2,7 +2,7 @@ import "./ReactionsModal.css";
import { NostrLink, socialGraphInstance, TaggedNostrEvent } from "@snort/system";
import { useEventReactions, useReactions } from "@snort/system-react";
import { useEffect, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CloseButton from "@/Components/Button/CloseButton";
@ -15,14 +15,12 @@ import { formatShort } from "@/Utils/Number";
import messages from "../../messages";
interface ReactionsModalProps {
show: boolean;
setShow(b: boolean): void;
onClose(): void;
event: TaggedNostrEvent;
}
const ReactionsModal = ({ show, setShow, event }: ReactionsModalProps) => {
const ReactionsModal = ({ onClose, event }: ReactionsModalProps) => {
const { formatMessage } = useIntl();
const onClose = () => setShow(false);
const link = NostrLink.fromEvent(event);
@ -59,12 +57,6 @@ const ReactionsModal = ({ show, setShow, event }: ReactionsModalProps) => {
const [tab, setTab] = useState(tabs[0]);
useEffect(() => {
if (!show) {
setTab(tabs[0]);
}
}, [show, tabs]);
const renderReactionItem = (ev, icon, size) => (
<div key={ev.id} className="reactions-item">
<div className="reaction-icon">
@ -74,7 +66,7 @@ const ReactionsModal = ({ show, setShow, event }: ReactionsModalProps) => {
</div>
);
return show ? (
return (
<Modal id="reactions" className="reactions-modal" onClose={onClose}>
<CloseButton onClick={onClose} className="absolute right-4 top-3" />
<div className="reactions-header">
@ -110,7 +102,7 @@ const ReactionsModal = ({ show, setShow, event }: ReactionsModalProps) => {
{tab.value === 3 && dislikes.map(ev => renderReactionItem(ev, "dislike"))}
</div>
</Modal>
) : null;
);
};
export default ReactionsModal;

View File

@ -1,13 +1,14 @@
import { ParsedZap } from "@snort/system";
import { useMemo } from "react";
import React, { useMemo } from "react";
import { AvatarGroup } from "@/Components/User/AvatarGroup";
import { dedupe } from "@/Utils";
interface ZapsSummaryProps {
zaps: ParsedZap[];
onClick: () => void;
}
export const ZapsSummary = ({ zaps }: ZapsSummaryProps) => {
export const ZapsSummary = ({ zaps, onClick }: ZapsSummaryProps) => {
const sortedZappers = useMemo(() => {
const pub = [...zaps.filter(z => z.sender && z.valid)];
const priv = [...zaps.filter(z => !z.sender && z.valid)];
@ -19,11 +20,16 @@ export const ZapsSummary = ({ zaps }: ZapsSummaryProps) => {
return null;
}
const myOnClick = (e: React.MouseEvent) => {
e.stopPropagation();
onClick();
};
return (
<div className="zaps-summary">
<div className="zaps-summary" onClick={myOnClick}>
<div className={`top-zap`}>
<div className="summary">
<AvatarGroup ids={sortedZappers} />
<AvatarGroup ids={sortedZappers} onClick={() => {}} />
{zaps.length > 3 && <div className="hidden md:flex -ml-2">+{zaps.length - 3}</div>}
</div>
</div>

View File

@ -1,6 +1,6 @@
import "./Modal.css";
import { ReactNode, useEffect } from "react";
import React, { ReactNode, useEffect } from "react";
import { createPortal } from "react-dom";
export interface ModalProps {
@ -60,12 +60,21 @@ export default function Modal(props: ModalProps) {
};
}, []);
const handleBackdropClick = (e: React.MouseEvent) => {
e.stopPropagation();
props.onClose?.(e);
};
return createPortal(
<div
className={props.className === "hidden" ? props.className : `modal ${props.className || ""}`}
onClick={props.onClose}>
onMouseDown={handleBackdropClick}
onClick={e => {
e.stopPropagation();
}}>
<div
className={props.bodyClassName || "modal-body"}
onMouseDown={e => e.stopPropagation()}
onClick={e => {
e.stopPropagation();
props.onClick?.(e);

View File

@ -3,10 +3,10 @@ import React from "react";
import ProfileImage from "@/Components/User/ProfileImage";
export function AvatarGroup({ ids }: { ids: HexKey[] }) {
export function AvatarGroup({ ids, onClick }: { ids: HexKey[]; onClick?: () => void }) {
return ids.map((a, index) => (
<div className={`inline-block ${index > 0 ? "-ml-5" : ""}`} key={a} style={{ zIndex: ids.length - index }}>
<ProfileImage showFollowDistance={false} pubkey={a} size={24} showUsername={false} />
<ProfileImage link="" onClick={onClick} showFollowDistance={false} pubkey={a} size={24} showUsername={false} />
</div>
));
}