open reactions modal from note footer zappers list
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
26e12d1c0b
commit
7ee210da16
@ -20,9 +20,10 @@ import { useWallet } from "@/Wallet";
|
|||||||
export interface ZapIconProps {
|
export interface ZapIconProps {
|
||||||
ev: TaggedNostrEvent;
|
ev: TaggedNostrEvent;
|
||||||
zaps: Array<ParsedZap>;
|
zaps: Array<ParsedZap>;
|
||||||
|
onClickZappers?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FooterZapButton = ({ ev, zaps }: ZapIconProps) => {
|
export const FooterZapButton = ({ ev, zaps, onClickZappers }: ZapIconProps) => {
|
||||||
const {
|
const {
|
||||||
publicKey,
|
publicKey,
|
||||||
readonly,
|
readonly,
|
||||||
@ -142,7 +143,7 @@ export const FooterZapButton = ({ ev, zaps }: ZapIconProps) => {
|
|||||||
value={zapTotal}
|
value={zapTotal}
|
||||||
onClick={fastZap}
|
onClick={fastZap}
|
||||||
/>
|
/>
|
||||||
<ZapsSummary zaps={zaps} />
|
<ZapsSummary zaps={zaps} onClick={onClickZappers} />
|
||||||
</div>
|
</div>
|
||||||
{showZapModal && (
|
{showZapModal && (
|
||||||
<ZapModal
|
<ZapModal
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||||
import { useEventReactions, useReactions } from "@snort/system-react";
|
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 { FooterZapButton } from "@/Components/Event/Note/NoteFooter/FooterZapButton";
|
||||||
import { LikeButton } from "@/Components/Event/Note/NoteFooter/LikeButton";
|
import { LikeButton } from "@/Components/Event/Note/NoteFooter/LikeButton";
|
||||||
import { PowIcon } from "@/Components/Event/Note/NoteFooter/PowIcon";
|
import { PowIcon } from "@/Components/Event/Note/NoteFooter/PowIcon";
|
||||||
import { ReplyButton } from "@/Components/Event/Note/NoteFooter/ReplyButton";
|
import { ReplyButton } from "@/Components/Event/Note/NoteFooter/ReplyButton";
|
||||||
import { RepostButton } from "@/Components/Event/Note/NoteFooter/RepostButton";
|
import { RepostButton } from "@/Components/Event/Note/NoteFooter/RepostButton";
|
||||||
|
import ReactionsModal from "@/Components/Event/Note/ReactionsModal";
|
||||||
import useLogin from "@/Hooks/useLogin";
|
import useLogin from "@/Hooks/useLogin";
|
||||||
|
|
||||||
export interface NoteFooterProps {
|
export interface NoteFooterProps {
|
||||||
@ -18,6 +19,7 @@ export default function NoteFooter(props: NoteFooterProps) {
|
|||||||
const { ev } = props;
|
const { ev } = props;
|
||||||
const link = useMemo(() => NostrLink.fromEvent(ev), [ev.id]);
|
const link = useMemo(() => NostrLink.fromEvent(ev), [ev.id]);
|
||||||
const ids = useMemo(() => [link], [link]);
|
const ids = useMemo(() => [link], [link]);
|
||||||
|
const [showReactions, setShowReactions] = useState(false);
|
||||||
|
|
||||||
const related = useReactions("note:reactions", ids, undefined, false);
|
const related = useReactions("note:reactions", ids, undefined, false);
|
||||||
const { reactions, zaps, reposts } = useEventReactions(link, related);
|
const { reactions, zaps, reposts } = useEventReactions(link, related);
|
||||||
@ -35,7 +37,8 @@ export default function NoteFooter(props: NoteFooterProps) {
|
|||||||
{!readonly && <RepostButton ev={ev} reposts={reposts} />}
|
{!readonly && <RepostButton ev={ev} reposts={reposts} />}
|
||||||
{prefs.enableReactions && <LikeButton ev={ev} positiveReactions={positive} />}
|
{prefs.enableReactions && <LikeButton ev={ev} positiveReactions={positive} />}
|
||||||
{CONFIG.showPowIcon && <PowIcon ev={ev} />}
|
{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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ export default function NoteHeader(props: {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ReactionsModal show={showReactions} setShow={setShowReactions} event={ev} />
|
{showReactions && <ReactionsModal onClose={() => setShowReactions(false)} event={ev} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import "./ReactionsModal.css";
|
|||||||
|
|
||||||
import { NostrLink, socialGraphInstance, TaggedNostrEvent } from "@snort/system";
|
import { NostrLink, socialGraphInstance, TaggedNostrEvent } from "@snort/system";
|
||||||
import { useEventReactions, useReactions } from "@snort/system-react";
|
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 { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import CloseButton from "@/Components/Button/CloseButton";
|
import CloseButton from "@/Components/Button/CloseButton";
|
||||||
@ -15,14 +15,12 @@ import { formatShort } from "@/Utils/Number";
|
|||||||
import messages from "../../messages";
|
import messages from "../../messages";
|
||||||
|
|
||||||
interface ReactionsModalProps {
|
interface ReactionsModalProps {
|
||||||
show: boolean;
|
onClose(): void;
|
||||||
setShow(b: boolean): void;
|
|
||||||
event: TaggedNostrEvent;
|
event: TaggedNostrEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReactionsModal = ({ show, setShow, event }: ReactionsModalProps) => {
|
const ReactionsModal = ({ onClose, event }: ReactionsModalProps) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const onClose = () => setShow(false);
|
|
||||||
|
|
||||||
const link = NostrLink.fromEvent(event);
|
const link = NostrLink.fromEvent(event);
|
||||||
|
|
||||||
@ -59,12 +57,6 @@ const ReactionsModal = ({ show, setShow, event }: ReactionsModalProps) => {
|
|||||||
|
|
||||||
const [tab, setTab] = useState(tabs[0]);
|
const [tab, setTab] = useState(tabs[0]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!show) {
|
|
||||||
setTab(tabs[0]);
|
|
||||||
}
|
|
||||||
}, [show, tabs]);
|
|
||||||
|
|
||||||
const renderReactionItem = (ev, icon, size) => (
|
const renderReactionItem = (ev, icon, size) => (
|
||||||
<div key={ev.id} className="reactions-item">
|
<div key={ev.id} className="reactions-item">
|
||||||
<div className="reaction-icon">
|
<div className="reaction-icon">
|
||||||
@ -74,7 +66,7 @@ const ReactionsModal = ({ show, setShow, event }: ReactionsModalProps) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return show ? (
|
return (
|
||||||
<Modal id="reactions" className="reactions-modal" onClose={onClose}>
|
<Modal id="reactions" className="reactions-modal" onClose={onClose}>
|
||||||
<CloseButton onClick={onClose} className="absolute right-4 top-3" />
|
<CloseButton onClick={onClose} className="absolute right-4 top-3" />
|
||||||
<div className="reactions-header">
|
<div className="reactions-header">
|
||||||
@ -110,7 +102,7 @@ const ReactionsModal = ({ show, setShow, event }: ReactionsModalProps) => {
|
|||||||
{tab.value === 3 && dislikes.map(ev => renderReactionItem(ev, "dislike"))}
|
{tab.value === 3 && dislikes.map(ev => renderReactionItem(ev, "dislike"))}
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
) : null;
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ReactionsModal;
|
export default ReactionsModal;
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { ParsedZap } from "@snort/system";
|
import { ParsedZap } from "@snort/system";
|
||||||
import { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
|
|
||||||
import { AvatarGroup } from "@/Components/User/AvatarGroup";
|
import { AvatarGroup } from "@/Components/User/AvatarGroup";
|
||||||
import { dedupe } from "@/Utils";
|
import { dedupe } from "@/Utils";
|
||||||
|
|
||||||
interface ZapsSummaryProps {
|
interface ZapsSummaryProps {
|
||||||
zaps: ParsedZap[];
|
zaps: ParsedZap[];
|
||||||
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
export const ZapsSummary = ({ zaps }: ZapsSummaryProps) => {
|
export const ZapsSummary = ({ zaps, onClick }: ZapsSummaryProps) => {
|
||||||
const sortedZappers = useMemo(() => {
|
const sortedZappers = useMemo(() => {
|
||||||
const pub = [...zaps.filter(z => z.sender && z.valid)];
|
const pub = [...zaps.filter(z => z.sender && z.valid)];
|
||||||
const priv = [...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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const myOnClick = (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onClick();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="zaps-summary">
|
<div className="zaps-summary" onClick={myOnClick}>
|
||||||
<div className={`top-zap`}>
|
<div className={`top-zap`}>
|
||||||
<div className="summary">
|
<div className="summary">
|
||||||
<AvatarGroup ids={sortedZappers} />
|
<AvatarGroup ids={sortedZappers} onClick={() => {}} />
|
||||||
{zaps.length > 3 && <div className="hidden md:flex -ml-2">+{zaps.length - 3}</div>}
|
{zaps.length > 3 && <div className="hidden md:flex -ml-2">+{zaps.length - 3}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./Modal.css";
|
import "./Modal.css";
|
||||||
|
|
||||||
import { ReactNode, useEffect } from "react";
|
import React, { ReactNode, useEffect } from "react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
export interface ModalProps {
|
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(
|
return createPortal(
|
||||||
<div
|
<div
|
||||||
className={props.className === "hidden" ? props.className : `modal ${props.className || ""}`}
|
className={props.className === "hidden" ? props.className : `modal ${props.className || ""}`}
|
||||||
onClick={props.onClose}>
|
onMouseDown={handleBackdropClick}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}>
|
||||||
<div
|
<div
|
||||||
className={props.bodyClassName || "modal-body"}
|
className={props.bodyClassName || "modal-body"}
|
||||||
|
onMouseDown={e => e.stopPropagation()}
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
props.onClick?.(e);
|
props.onClick?.(e);
|
||||||
|
@ -3,10 +3,10 @@ import React from "react";
|
|||||||
|
|
||||||
import ProfileImage from "@/Components/User/ProfileImage";
|
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) => (
|
return ids.map((a, index) => (
|
||||||
<div className={`inline-block ${index > 0 ? "-ml-5" : ""}`} key={a} style={{ zIndex: ids.length - 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>
|
</div>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user