feat: a link profile image

This commit is contained in:
Kieran 2023-05-10 13:29:07 +01:00
parent 2ed38d1b97
commit ec4e6498d3
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
10 changed files with 97 additions and 127 deletions

View File

@ -19,4 +19,5 @@
border: 1px solid var(--font-tertiary-color);
margin-top: auto;
margin-bottom: auto;
overflow: hidden;
}

View File

@ -327,10 +327,9 @@ export default function Note(props: NoteProps) {
{options.showHeader && (
<div className="header flex">
<ProfileImage
autoWidth={false}
pubkey={ev.pubkey}
subHeader={replyTag() ?? undefined}
linkToProfile={opt?.canClick === undefined}
link={opt?.canClick === undefined ? undefined : ""}
/>
{(options.showTime || options.showBookmarked) && (
<div className="info">

View File

@ -293,7 +293,7 @@ export function NoteCreator() {
ev.stopPropagation = true;
LoginStore.switchAccount(a);
}}>
<ProfileImage pubkey={a} linkToProfile={false} />
<ProfileImage pubkey={a} link={""} />
</MenuItem>
));
}

View File

@ -1,6 +1,10 @@
.pfp {
display: flex;
display: grid;
grid-template-columns: min-content auto;
align-items: center;
text-decoration: none;
user-select: none;
min-width: 0;
}
.pfp .avatar-wrapper {
@ -14,7 +18,7 @@
cursor: pointer;
}
.pfp a {
a.pfp {
text-decoration: none;
}
@ -25,6 +29,10 @@
font-weight: 600;
}
.pfp .subheader .about {
max-width: calc(100vw - 140px);
.pfp .profile-name {
max-width: stretch;
}
.pfp a {
text-decoration: none;
}

View File

@ -1,7 +1,6 @@
import "./ProfileImage.css";
import { useMemo } from "react";
import { useNavigate } from "react-router-dom";
import { HexKey, NostrPrefix } from "@snort/nostr";
import { useUserProfile } from "Hooks/useUserProfile";
@ -9,7 +8,7 @@ import { hexToBech32, profileLink } from "Util";
import Avatar from "Element/Avatar";
import Nip05 from "Element/Nip05";
import { MetadataCache } from "Cache";
import usePageWidth from "Hooks/usePageWidth";
import { Link } from "react-router-dom";
export interface ProfileImageProps {
pubkey: HexKey;
@ -17,10 +16,8 @@ export interface ProfileImageProps {
showUsername?: boolean;
className?: string;
link?: string;
autoWidth?: boolean;
defaultNip?: string;
verifyNip?: boolean;
linkToProfile?: boolean;
overrideUsername?: string;
}
@ -30,50 +27,32 @@ export default function ProfileImage({
showUsername = true,
className,
link,
autoWidth = true,
defaultNip,
verifyNip,
linkToProfile = true,
overrideUsername,
}: ProfileImageProps) {
const navigate = useNavigate();
const user = useUserProfile(pubkey);
const nip05 = defaultNip ? defaultNip : user?.nip05;
const width = usePageWidth();
const name = useMemo(() => {
return overrideUsername ?? getDisplayName(user, pubkey);
}, [user, pubkey, overrideUsername]);
if (!pubkey && !link) {
link = "#";
}
const onAvatarClick = () => {
if (linkToProfile) {
navigate(link ?? profileLink(pubkey));
}
};
return (
<div className={`pfp f-ellipsis${className ? ` ${className}` : ""}`} onClick={onAvatarClick}>
<Link className={`pfp${className ? ` ${className}` : ""}`} to={link === undefined ? profileLink(pubkey) : ""}>
<div className="avatar-wrapper">
<Avatar user={user} />
</div>
{showUsername && (
<div className="profile-name">
<div className="f-ellipsis">
<div className="username">
<div className="display-name">
<div>{name.trim()}</div>
{nip05 && <Nip05 nip05={nip05} pubkey={pubkey} verifyNip={verifyNip} />}
</div>
</div>
<div className="subheader" style={{ width: autoWidth ? width - 190 : "" }}>
{subHeader}
<div>{name.trim()}</div>
{nip05 && <Nip05 nip05={nip05} pubkey={pubkey} verifyNip={verifyNip} />}
</div>
<div className="subheader">{subHeader}</div>
</div>
)}
</div>
</Link>
);
}

View File

@ -1,28 +1,23 @@
.reactions-modal .modal-body {
padding: 0;
max-width: 586px;
}
.reactions-view {
padding: 24px 32px;
background-color: #1b1b1b;
border-radius: 16px;
position: relative;
min-height: 33vh;
}
.light .reactions-view {
.light .reactions-modal .modal-body {
background-color: var(--note-bg);
}
@media (max-width: 720px) {
.reactions-view {
.reactions-modal .modal-body {
padding: 12px 16px;
margin-top: -160px;
max-width: calc(100vw - 32px);
}
}
.reactions-view .close {
.reactions-modal .modal-body .close {
position: absolute;
top: 12px;
right: 16px;
@ -30,18 +25,18 @@
cursor: pointer;
}
.reactions-view .close:hover {
.reactions-modal .modal-body .close:hover {
color: var(--font-tertiary-color);
}
.reactions-view .reactions-header {
.reactions-modal .modal-body .reactions-header {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 32px;
}
.reactions-view .reactions-header h2 {
.reactions-modal .modal-body .reactions-header h2 {
margin: 0;
flex-grow: 1;
font-weight: 600;
@ -49,26 +44,25 @@
line-height: 19px;
}
.reactions-view .body {
.reactions-modal .modal-body .body {
overflow: scroll;
height: 320px;
height: 40vh;
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* Firefox */
}
.reactions-view .body::-webkit-scrollbar {
.reactions-modal .modal-body .body::-webkit-scrollbar {
display: none;
}
.reactions-item {
display: flex;
flex-direction: row;
display: grid;
grid-template-columns: 52px auto;
align-items: center;
margin-bottom: 24px;
}
.reactions-item .reaction-icon {
width: 52px;
display: flex;
align-items: center;
justify-content: center;
@ -93,12 +87,8 @@
line-height: 17px;
}
.reactions-item .zap-comment {
width: 332px;
}
@media (max-width: 520px) {
.reactions-view .tab.disabled {
.reactions-modal .modal-body .tab.disabled {
display: none;
}
}

View File

@ -2,7 +2,6 @@ import "./Reactions.css";
import { useState, useMemo, useEffect } from "react";
import { useIntl, FormattedMessage } from "react-intl";
import { TaggedRawEvent } from "@snort/nostr";
import { formatShort } from "Number";
@ -75,72 +74,66 @@ const Reactions = ({ show, setShow, positive, negative, reposts, zaps }: Reactio
return show ? (
<Modal className="reactions-modal" onClose={onClose}>
<div className="reactions-view">
<div className="close" onClick={onClose}>
<Icon name="close" />
</div>
<div className="reactions-header">
<h2>
<FormattedMessage {...messages.ReactionsCount} values={{ n: total }} />
</h2>
</div>
<Tabs tabs={tabs} tab={tab} setTab={setTab} />
<div className="body" key={tab.value}>
{tab.value === 0 &&
likes.map(ev => {
return (
<div key={ev.id} className="reactions-item">
<div className="reaction-icon">{ev.content === "+" ? <Icon name="heart" /> : ev.content}</div>
<ProfileImage autoWidth={false} pubkey={ev.pubkey} />
</div>
);
})}
{tab.value === 1 &&
zaps.map(z => {
return (
z.sender && (
<div key={z.id} className="reactions-item">
<div className="zap-reaction-icon">
<Icon name="zap" size={20} />
<span className="zap-amount">{formatShort(z.amount)}</span>
</div>
<ProfileImage
autoWidth={false}
pubkey={z.anonZap ? "" : z.sender}
subHeader={
<div className="f-ellipsis zap-comment" title={z.content}>
{z.content}
</div>
}
overrideUsername={z.anonZap ? formatMessage({ defaultMessage: "Anonymous" }) : undefined}
/>
<div className="close" onClick={onClose}>
<Icon name="close" />
</div>
<div className="reactions-header">
<h2>
<FormattedMessage {...messages.ReactionsCount} values={{ n: total }} />
</h2>
</div>
<Tabs tabs={tabs} tab={tab} setTab={setTab} />
<div className="body" key={tab.value}>
{tab.value === 0 &&
likes.map(ev => {
return (
<div key={ev.id} className="reactions-item">
<div className="reaction-icon">{ev.content === "+" ? <Icon name="heart" /> : ev.content}</div>
<ProfileImage pubkey={ev.pubkey} />
</div>
);
})}
{tab.value === 1 &&
zaps.map(z => {
return (
z.sender && (
<div key={z.id} className="reactions-item">
<div className="zap-reaction-icon">
<Icon name="zap" size={20} />
<span className="zap-amount">{formatShort(z.amount)}</span>
</div>
)
);
})}
{tab.value === 2 &&
reposts.map(ev => {
return (
<div key={ev.id} className="reactions-item">
<div className="reaction-icon">
<Icon name="repost" size={16} />
</div>
<ProfileImage autoWidth={false} pubkey={ev.pubkey} />
<ProfileImage
pubkey={z.anonZap ? "" : z.sender}
subHeader={<div title={z.content}>{z.content}</div>}
link={z.anonZap ? "" : undefined}
overrideUsername={z.anonZap ? formatMessage({ defaultMessage: "Anonymous" }) : undefined}
/>
</div>
);
})}
{tab.value === 3 &&
dislikes.map(ev => {
return (
<div key={ev.id} className="reactions-item">
<div className="reaction-icon">
<Icon name="dislike" />
</div>
<ProfileImage autoWidth={false} pubkey={ev.pubkey} />
)
);
})}
{tab.value === 2 &&
reposts.map(ev => {
return (
<div key={ev.id} className="reactions-item">
<div className="reaction-icon">
<Icon name="repost" size={16} />
</div>
);
})}
</div>
<ProfileImage pubkey={ev.pubkey} />
</div>
);
})}
{tab.value === 3 &&
dislikes.map(ev => {
return (
<div key={ev.id} className="reactions-item f-ellipsis">
<div className="reaction-icon">
<Icon name="dislike" />
</div>
<ProfileImage pubkey={ev.pubkey} />
</div>
);
})}
</div>
</Modal>
) : null;

View File

@ -119,7 +119,7 @@ const Timeline = (props: TimelineProps) => {
<>
<div className="card latest-notes pointer" onClick={() => onShowLatest()} ref={ref}>
{latestAuthors.slice(0, 3).map(p => {
return <ProfileImage pubkey={p} showUsername={false} linkToProfile={false} />;
return <ProfileImage pubkey={p} showUsername={false} link={""} />;
})}
<FormattedMessage
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
@ -130,7 +130,7 @@ const Timeline = (props: TimelineProps) => {
{!inView && (
<div className="card latest-notes latest-notes-fixed pointer fade-in" onClick={() => onShowLatest(true)}>
{latestAuthors.slice(0, 3).map(p => {
return <ProfileImage pubkey={p} showUsername={false} linkToProfile={false} />;
return <ProfileImage pubkey={p} showUsername={false} link={""} />;
})}
<FormattedMessage
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"

View File

@ -107,8 +107,8 @@ const Zap = ({ zap, showZapped = true }: { zap: ParsedZap; showZapped?: boolean
return valid && sender ? (
<div className="zap note card">
<div className="header">
<ProfileImage autoWidth={false} pubkey={sender} />
{receiver !== pubKey && showZapped && <ProfileImage autoWidth={false} pubkey={unwrap(receiver)} />}
<ProfileImage pubkey={sender} />
{receiver !== pubKey && showZapped && <ProfileImage pubkey={unwrap(receiver)} />}
<div className="amount">
<span className="amount-number">
<FormattedMessage {...messages.Sats} values={{ n: formatShort(amount ?? 0) }} />
@ -151,7 +151,6 @@ export const ZapsSummary = ({ zaps }: ZapsSummaryProps) => {
<div className="summary">
{sender && (
<ProfileImage
autoWidth={false}
pubkey={anonZap ? "" : sender}
overrideUsername={anonZap ? formatMessage({ defaultMessage: "Anonymous" }) : undefined}
/>

View File

@ -42,6 +42,7 @@
html {
scroll-behavior: smooth;
-webkit-tap-highlight-color: transparent;
}
html.light {