forked from Kieran/snort
show zapper avatars on the same notefooter row
This commit is contained in:
parent
ee01623bf1
commit
3a73d53b5c
@ -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;
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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} />}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,7 @@ export default function NoteFooter(props: NoteFooterProps) {
|
|||||||
const targets = getZapTarget();
|
const targets = getZapTarget();
|
||||||
if (targets) {
|
if (targets) {
|
||||||
return (
|
return (
|
||||||
|
<div className="flex flex-row gap-4 items-center">
|
||||||
<AsyncFooterIcon
|
<AsyncFooterIcon
|
||||||
className={didZap ? "reacted text-nostr-orange" : "hover:text-nostr-orange"}
|
className={didZap ? "reacted text-nostr-orange" : "hover:text-nostr-orange"}
|
||||||
{...longPress()}
|
{...longPress()}
|
||||||
@ -201,9 +202,11 @@ export default function NoteFooter(props: NoteFooterProps) {
|
|||||||
value={zapTotal}
|
value={zapTotal}
|
||||||
onClick={e => fastZap(e)}
|
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">
|
|
||||||
<div className="footer-reactions">
|
|
||||||
{replyIcon()}
|
{replyIcon()}
|
||||||
{repostIcon()}
|
{repostIcon()}
|
||||||
{reactionIcon()}
|
{reactionIcon()}
|
||||||
{tipButton()}
|
|
||||||
{powIcon()}
|
{powIcon()}
|
||||||
</div>
|
{tipButton()}
|
||||||
<SendSats targets={getZapTarget()} onClose={() => setTip(false)} show={tip} note={ev.id} allocatePool={true} />
|
<SendSats targets={getZapTarget()} onClose={() => setTip(false)} show={tip} note={ev.id} allocatePool={true} />
|
||||||
</div>
|
</div>
|
||||||
<ZapsSummary zaps={zaps} />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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">
|
||||||
{sender && (
|
<AvatarGroup ids={sortedZappers} />
|
||||||
<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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
12
packages/app/src/Components/User/AvatarGroup.tsx
Normal file
12
packages/app/src/Components/User/AvatarGroup.tsx
Normal 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>
|
||||||
|
));
|
||||||
|
}
|
@ -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">
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user