feat: profile cards on mentions
This commit is contained in:
parent
d50979b8ae
commit
7a1df4a178
@ -1,17 +1,25 @@
|
||||
import { NostrLink, NostrPrefix } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
import { useHover } from "@uidotdev/usehooks";
|
||||
|
||||
import DisplayName from "Element/User/DisplayName";
|
||||
import { ProfileCard } from "Element/User/ProfileCard";
|
||||
import { ProfileLink } from "Element/User/ProfileLink";
|
||||
|
||||
export default function Mention({ link }: { link: NostrLink }) {
|
||||
const [ref, hovering] = useHover<HTMLAnchorElement>();
|
||||
const profile = useUserProfile(link.id);
|
||||
|
||||
if (link.type !== NostrPrefix.Profile && link.type !== NostrPrefix.PublicKey) return;
|
||||
|
||||
return (
|
||||
<ProfileLink pubkey={link.id} user={profile} onClick={e => e.stopPropagation()}>
|
||||
@<DisplayName user={profile} pubkey={link.id} />
|
||||
</ProfileLink>
|
||||
<>
|
||||
<ProfileLink pubkey={link.id} user={profile} onClick={e => e.stopPropagation()}>
|
||||
<span ref={ref}>
|
||||
@<DisplayName user={profile} pubkey={link.id} />
|
||||
</span>
|
||||
</ProfileLink>
|
||||
<ProfileCard pubkey={link.id} user={profile} show={hovering} ref={ref} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -33,7 +33,10 @@ export function getDisplayNameOrPlaceHolder(user: UserMetadata | undefined, pubk
|
||||
const DisplayName = ({ pubkey, user }: DisplayNameProps) => {
|
||||
const [name, isPlaceHolder] = useMemo(() => getDisplayNameOrPlaceHolder(user, pubkey), [user, pubkey]);
|
||||
|
||||
return <span className={isPlaceHolder ? "placeholder" : ""}>{name}</span>;
|
||||
if (isPlaceHolder) {
|
||||
return <span className="placeholder">{name}</span>;
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
export default DisplayName;
|
||||
|
11
packages/app/src/Element/User/ProfileCard.css
Normal file
11
packages/app/src/Element/User/ProfileCard.css
Normal file
@ -0,0 +1,11 @@
|
||||
.profile-card {
|
||||
width: 360px;
|
||||
border-radius: 16px;
|
||||
background: var(--gray-superdark);
|
||||
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.profile-card > div {
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
}
|
86
packages/app/src/Element/User/ProfileCard.tsx
Normal file
86
packages/app/src/Element/User/ProfileCard.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import "./ProfileCard.css";
|
||||
|
||||
import { ControlledMenu } from "@szhsin/react-menu";
|
||||
import { UserMetadata } from "@snort/system";
|
||||
|
||||
import FollowButton from "./FollowButton";
|
||||
import ProfileImage from "./ProfileImage";
|
||||
import { UserWebsiteLink } from "./UserWebsiteLink";
|
||||
import Text from "Element/Text";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface RectElement {
|
||||
getBoundingClientRect(): {
|
||||
left: number;
|
||||
right: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
export function ProfileCard({
|
||||
pubkey,
|
||||
user,
|
||||
show,
|
||||
ref,
|
||||
delay,
|
||||
}: {
|
||||
pubkey: string;
|
||||
user?: UserMetadata;
|
||||
show: boolean;
|
||||
ref: React.RefObject<Element | RectElement>;
|
||||
delay?: number;
|
||||
}) {
|
||||
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
||||
const [t, setT] = useState<ReturnType<typeof setTimeout>>();
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
const tn = setTimeout(() => {
|
||||
setShowProfileMenu(true);
|
||||
}, delay ?? 1000);
|
||||
setT(tn);
|
||||
} else {
|
||||
if (t) {
|
||||
clearTimeout(t);
|
||||
setT(undefined);
|
||||
}
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
if (!show && !showProfileMenu) return;
|
||||
return (
|
||||
<ControlledMenu
|
||||
state={showProfileMenu ? "open" : "closed"}
|
||||
anchorRef={ref}
|
||||
menuClassName="profile-card"
|
||||
onClose={() => setShowProfileMenu(false)}
|
||||
align="end">
|
||||
<div className="flex flex-col g8">
|
||||
<div className="flex justify-between">
|
||||
<ProfileImage pubkey={""} profile={user} showProfileCard={false} link="" />
|
||||
<div className="flex g8">
|
||||
{/*<button type="button" onClick={() => {
|
||||
LoginStore.loginWithPubkey(pubkey, LoginSessionType.PublicKey, undefined, undefined, undefined, true);
|
||||
}}>
|
||||
<FormattedMessage defaultMessage="Stalk" />
|
||||
</button>*/}
|
||||
<FollowButton pubkey={pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
<Text
|
||||
id={`profile-card-${pubkey}`}
|
||||
content={user?.about ?? ""}
|
||||
creator={pubkey}
|
||||
tags={[]}
|
||||
disableMedia={true}
|
||||
disableLinkPreview={true}
|
||||
truncate={250}
|
||||
/>
|
||||
<UserWebsiteLink user={user} />
|
||||
</div>
|
||||
</ControlledMenu>
|
||||
);
|
||||
}
|
@ -43,15 +43,3 @@ a.pfp {
|
||||
background-color: var(--gray-superdark);
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
width: 360px;
|
||||
border-radius: 16px;
|
||||
background: var(--gray-superdark);
|
||||
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.profile-card > div {
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import "./ProfileImage.css";
|
||||
|
||||
import React, { ReactNode, useEffect, useState } from "react";
|
||||
import React, { ReactNode } from "react";
|
||||
import { HexKey, UserMetadata } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
import { useHover } from "@uidotdev/usehooks";
|
||||
import { ControlledMenu } from "@szhsin/react-menu";
|
||||
import classNames from "classnames";
|
||||
|
||||
import Avatar from "Element/User/Avatar";
|
||||
@ -12,10 +11,8 @@ import Nip05 from "Element/User/Nip05";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import Icon from "Icons/Icon";
|
||||
import DisplayName from "./DisplayName";
|
||||
import Text from "Element/Text";
|
||||
import FollowButton from "Element/User/FollowButton";
|
||||
import { UserWebsiteLink } from "Element/User/UserWebsiteLink";
|
||||
import { ProfileLink } from "./ProfileLink";
|
||||
import { ProfileCard } from "./ProfileCard";
|
||||
|
||||
export interface ProfileImageProps {
|
||||
pubkey: HexKey;
|
||||
@ -57,22 +54,6 @@ export default function ProfileImage({
|
||||
const { follows } = useLogin();
|
||||
const doesFollow = follows.item.includes(pubkey);
|
||||
const [ref, hovering] = useHover<HTMLDivElement>();
|
||||
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
||||
const [t, setT] = useState<ReturnType<typeof setTimeout>>();
|
||||
|
||||
useEffect(() => {
|
||||
if (hovering) {
|
||||
const tn = setTimeout(() => {
|
||||
setShowProfileMenu(true);
|
||||
}, 1000);
|
||||
setT(tn);
|
||||
} else {
|
||||
if (t) {
|
||||
clearTimeout(t);
|
||||
setT(undefined);
|
||||
}
|
||||
}
|
||||
}, [hovering]);
|
||||
|
||||
function handleClick(e: React.MouseEvent) {
|
||||
if (link === "") {
|
||||
@ -118,38 +99,8 @@ export default function ProfileImage({
|
||||
}
|
||||
|
||||
function profileCard() {
|
||||
if (showProfileCard ?? true) {
|
||||
return (
|
||||
<ControlledMenu
|
||||
state={showProfileMenu ? "open" : "closed"}
|
||||
anchorRef={ref}
|
||||
menuClassName="profile-card"
|
||||
onClose={() => setShowProfileMenu(false)}>
|
||||
<div className="flex flex-col g8">
|
||||
<div className="flex justify-between">
|
||||
<ProfileImage pubkey={""} profile={user} showProfileCard={false} link="" />
|
||||
<div className="flex g8">
|
||||
{/*<button type="button" onClick={() => {
|
||||
LoginStore.loginWithPubkey(pubkey, LoginSessionType.PublicKey, undefined, undefined, undefined, true);
|
||||
}}>
|
||||
<FormattedMessage defaultMessage="Stalk" />
|
||||
</button>*/}
|
||||
<FollowButton pubkey={pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
<Text
|
||||
id={`profile-card-${pubkey}`}
|
||||
content={user?.about ?? ""}
|
||||
creator={pubkey}
|
||||
tags={[]}
|
||||
disableMedia={true}
|
||||
disableLinkPreview={true}
|
||||
truncate={250}
|
||||
/>
|
||||
<UserWebsiteLink user={user} />
|
||||
</div>
|
||||
</ControlledMenu>
|
||||
);
|
||||
if ((showProfileCard ?? true) && user) {
|
||||
return <ProfileCard pubkey={pubkey} user={user} show={hovering} ref={ref} />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user