show zapper avatars on the same notefooter row

This commit is contained in:
Martti Malmi 2024-01-11 12:00:53 +02:00
parent 536f8ddc5b
commit de6685ade3
10 changed files with 47 additions and 117 deletions

View File

@ -57,21 +57,6 @@
margin-top: 16px; margin-top: 16px;
} }
.note .footer .footer-reactions {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-left: auto;
gap: 48px;
}
@media (min-width: 720px) {
.note .footer .footer-reactions {
margin-left: 0;
}
}
.note > .header img:hover, .note > .header img:hover,
.note > .header .name > .reply:hover { .note > .header .name > .reply:hover {
cursor: pointer; cursor: pointer;

View File

@ -46,22 +46,3 @@
width: -webkit-fill-available; width: -webkit-fill-available;
aspect-ratio: 16 / 9; aspect-ratio: 16 / 9;
} }
.long-form-note .footer {
display: flex;
}
.long-form-note .footer .footer-reactions {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-left: auto;
gap: 48px;
}
@media (min-width: 720px) {
.long-form-note .footer .footer-reactions {
margin-left: 0;
}
}

View File

@ -55,8 +55,12 @@ export function Note(props: NoteProps) {
<NoteText {...props} translated={translated} showTranslation={showTranslation} /> <NoteText {...props} translated={translated} showTranslation={showTranslation} />
{translated && <TranslationInfo translated={translated} setShowTranslation={setShowTranslation} />} {translated && <TranslationInfo translated={translated} setShowTranslation={setShowTranslation} />}
{ev.kind === EventKind.Polls && <Poll ev={ev} />} {ev.kind === EventKind.Polls && <Poll ev={ev} />}
{optionsMerged.showFooter && (
<div className="mt-4">
<NoteFooter ev={ev} replies={props.threadChains?.get(chainKey(ev))?.length} />
</div>
)}
</div> </div>
{optionsMerged.showFooter && <NoteFooter ev={ev} replies={props.threadChains?.get(chainKey(ev))?.length} />}
</> </>
); );
} }

View File

@ -193,17 +193,20 @@ export default function NoteFooter(props: NoteFooterProps) {
const targets = getZapTarget(); const targets = getZapTarget();
if (targets) { if (targets) {
return ( return (
<AsyncFooterIcon <div className="flex flex-row gap-4 items-center">
className={didZap ? "reacted text-nostr-orange" : "hover:text-nostr-orange"} <AsyncFooterIcon
{...longPress()} className={didZap ? "reacted text-nostr-orange" : "hover:text-nostr-orange"}
title={formatMessage({ defaultMessage: "Zap", id: "fBI91o" })} {...longPress()}
iconName={canFastZap ? "zapFast" : "zap"} title={formatMessage({ defaultMessage: "Zap", id: "fBI91o" })}
value={zapTotal} iconName={canFastZap ? "zapFast" : "zap"}
onClick={e => fastZap(e)} value={zapTotal}
/> onClick={e => fastZap(e)}
/>
<ZapsSummary zaps={zaps} />
</div>
); );
} }
return null; return <div className="w-[18px]"></div>;
} }
function repostIcon() { function repostIcon() {
@ -289,19 +292,14 @@ export default function NoteFooter(props: NoteFooterProps) {
}; };
return ( return (
<> <div className="flex flex-row justify-between gap-2 overflow-hidden w-[360px] flex-grow max-w-full h-6">
<div className="footer"> {replyIcon()}
<div className="footer-reactions"> {repostIcon()}
{replyIcon()} {reactionIcon()}
{repostIcon()} {powIcon()}
{reactionIcon()} {tipButton()}
{tipButton()} <SendSats targets={getZapTarget()} onClose={() => setTip(false)} show={tip} note={ev.id} allocatePool={true} />
{powIcon()} </div>
</div>
<SendSats targets={getZapTarget()} onClose={() => setTip(false)} show={tip} note={ev.id} allocatePool={true} />
</div>
<ZapsSummary zaps={zaps} />
</>
); );
} }

View File

@ -12,10 +12,6 @@
line-height: 27px; line-height: 27px;
} }
.thread-root.note > .footer {
padding-left: 0;
}
.thread-root.note { .thread-root.note {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
@ -26,8 +22,6 @@
border: 0; border: 0;
} }
.thread-note.note .zaps-summary,
.thread-note.note .footer,
.thread-note.note .body { .thread-note.note .body {
margin-left: 61px; margin-left: 61px;
} }

View File

@ -44,15 +44,6 @@
color: var(--font-secondary-color); color: var(--font-secondary-color);
} }
.zaps-summary {
display: flex;
flex-direction: row;
}
.note.thread-root .zaps-summary {
margin-left: 14px;
}
.top-zap { .top-zap {
font-size: 14px; font-size: 14px;
border: none; border: none;

View File

@ -1,50 +1,31 @@
import { ParsedZap } from "@snort/system"; import { ParsedZap } from "@snort/system";
import { useMemo } from "react"; import { useMemo } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import messages from "@/Components/messages"; import { AvatarGroup } from "@/Components/User/AvatarGroup";
import ProfileImage from "@/Components/User/ProfileImage"; import { dedupe } from "@/Utils";
interface ZapsSummaryProps { interface ZapsSummaryProps {
zaps: ParsedZap[]; zaps: ParsedZap[];
} }
export const ZapsSummary = ({ zaps }: ZapsSummaryProps) => { export const ZapsSummary = ({ zaps }: ZapsSummaryProps) => {
const { formatMessage } = useIntl(); const sortedZappers = useMemo(() => {
const sortedZaps = 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)];
pub.sort((a, b) => b.amount - a.amount); pub.sort((a, b) => b.amount - a.amount);
return pub.concat(priv); return dedupe(pub.concat(priv).map(z => z.sender)).slice(0, 3);
}, [zaps]); }, [zaps]);
if (zaps.length === 0) { if (zaps.length === 0) {
return null; return null;
} }
const [topZap, ...restZaps] = sortedZaps;
const { sender, amount, anonZap } = topZap;
return ( return (
<div className="zaps-summary"> <div className="zaps-summary">
{amount && ( <div className={`top-zap`}>
<div className={`top-zap`}> <div className="summary">
<div className="summary"> <AvatarGroup ids={sortedZappers} />
{sender && (
<ProfileImage
pubkey={anonZap ? "" : sender}
showFollowDistance={false}
overrideUsername={anonZap ? formatMessage({ defaultMessage: "Anonymous", id: "LXxsbk" }) : undefined}
/>
)}
{restZaps.length > 0 ? (
<FormattedMessage {...messages.Others} values={{ n: restZaps.length }} />
) : (
<FormattedMessage {...messages.Zapped} />
)}{" "}
<FormattedMessage {...messages.OthersZapped} values={{ n: restZaps.length }} />
</div>
</div> </div>
)} </div>
</div> </div>
); );
}; };

View File

@ -0,0 +1,12 @@
import { HexKey } from "@snort/system";
import React from "react";
import ProfileImage from "@/Components/User/ProfileImage";
export function AvatarGroup({ ids }: { ids: HexKey[] }) {
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} />
</div>
));
}

View File

@ -2,9 +2,9 @@ import { HexKey, socialGraphInstance } from "@snort/system";
import React, { Fragment, useMemo } from "react"; import React, { Fragment, useMemo } from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { AvatarGroup } from "@/Components/User/AvatarGroup";
import DisplayName from "@/Components/User/DisplayName"; import DisplayName from "@/Components/User/DisplayName";
import FollowDistanceIndicator from "@/Components/User/FollowDistanceIndicator"; import FollowDistanceIndicator from "@/Components/User/FollowDistanceIndicator";
import ProfileImage from "@/Components/User/ProfileImage";
import { ProfileLink } from "@/Components/User/ProfileLink"; import { ProfileLink } from "@/Components/User/ProfileLink";
const MAX_FOLLOWED_BY_FRIENDS = 3; const MAX_FOLLOWED_BY_FRIENDS = 3;
@ -19,17 +19,6 @@ export default function FollowedBy({ pubkey }: { pubkey: HexKey }) {
}; };
}, [pubkey, followDistance]); }, [pubkey, followDistance]);
const renderFollowedByFriends = () => {
return followedByFriendsArray.map((a, index) => (
<div
className={`inline-block ${index > 0 ? "-ml-5" : ""}`}
key={a}
style={{ zIndex: followedByFriendsArray.length - index }}>
<ProfileImage showFollowDistance={false} pubkey={a} size={24} showUsername={false} />
</div>
));
};
const renderFollowedByFriendsLinks = () => { const renderFollowedByFriendsLinks = () => {
return followedByFriendsArray.map((a, index) => ( return followedByFriendsArray.map((a, index) => (
<Fragment key={a}> <Fragment key={a}>
@ -45,7 +34,7 @@ export default function FollowedBy({ pubkey }: { pubkey: HexKey }) {
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<FollowDistanceIndicator className="p-2" pubkey={pubkey} /> <FollowDistanceIndicator className="p-2" pubkey={pubkey} />
{renderFollowedByFriends()} <AvatarGroup ids={followedByFriendsArray} />
</div> </div>
{totalFollowedByFriends > 0 && ( {totalFollowedByFriends > 0 && (
<div className="text-gray-light"> <div className="text-gray-light">

View File

@ -218,11 +218,6 @@ a.ext {
margin-bottom: -8px; margin-bottom: -8px;
} }
.card > .footer {
display: flex;
flex-direction: row;
}
.card .card-title { .card .card-title {
font-size: x-large; font-size: x-large;
font-weight: bold; font-weight: bold;