Merge remote-tracking branch 'mmalmi/main'

This commit is contained in:
2023-11-16 12:37:48 +00:00
parent 83ffe746b1
commit 3611af9dce
28 changed files with 997 additions and 51 deletions

View File

@ -448,7 +448,7 @@ export function NoteCreator() {
className="note-creator-icon"
link=""
showUsername={false}
showFollowingMark={false}
showFollowDistance={false}
/>
{note.pollOptions === undefined && !note.replyTo && (
<AsyncIcon

View File

@ -59,7 +59,7 @@ export const ZapsSummary = ({ zaps }: ZapsSummaryProps) => {
{sender && (
<ProfileImage
pubkey={anonZap ? "" : sender}
showFollowingMark={false}
showFollowDistance={false}
overrideUsername={anonZap ? formatMessage({ defaultMessage: "Anonymous" }) : undefined}
/>
)}

View File

@ -36,7 +36,7 @@ export function TimelineRenderer(props: TimelineRendererProps) {
<>
<div className="card latest-notes" onClick={() => props.showLatest(false)} ref={ref}>
{props.latest.slice(0, 3).map(p => {
return <ProfileImage pubkey={p} showUsername={false} link={""} showFollowingMark={false} />;
return <ProfileImage pubkey={p} showUsername={false} link={""} showFollowDistance={false} />;
})}
<FormattedMessage
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
@ -49,7 +49,7 @@ export function TimelineRenderer(props: TimelineRendererProps) {
className="card latest-notes latest-notes-fixed pointer fade-in"
onClick={() => props.showLatest(true)}>
{props.latest.slice(0, 3).map(p => {
return <ProfileImage pubkey={p} showUsername={false} link={""} showFollowingMark={false} />;
return <ProfileImage pubkey={p} showUsername={false} link={""} showFollowDistance={false} />;
})}
<FormattedMessage
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"

View File

@ -10,6 +10,34 @@ export interface ModalProps {
children: ReactNode;
}
let scrollbarWidth: number | null = null;
const getScrollbarWidth = () => {
if (scrollbarWidth !== null) {
return scrollbarWidth;
}
const outer = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.width = "100px";
document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = "scroll";
const inner = document.createElement("div");
inner.style.width = "100%";
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
outer.parentNode?.removeChild(outer);
scrollbarWidth = widthNoScroll - widthWithScroll;
return scrollbarWidth;
};
export default function Modal(props: ModalProps) {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape" && props.onClose) {
@ -19,10 +47,13 @@ export default function Modal(props: ModalProps) {
useEffect(() => {
document.body.classList.add("scroll-lock");
document.body.style.paddingRight = `${getScrollbarWidth()}px`;
document.addEventListener("keydown", handleKeyDown);
return () => {
document.body.classList.remove("scroll-lock");
document.body.style.paddingRight = "";
document.removeEventListener("keydown", handleKeyDown);
};
}, []);

View File

@ -16,6 +16,7 @@ export default function SearchBox() {
const { formatMessage } = useIntl();
const [search, setSearch] = useState("");
const [searching, setSearching] = useState(false);
const [isFocused, setIsFocused] = useState(false);
const navigate = useNavigate();
const location = useLocation();
@ -120,13 +121,15 @@ export default function SearchBox() {
value={search}
onChange={handleChange}
onKeyDown={handleKeyDown}
onFocus={() => setIsFocused(true)}
onBlur={() => setTimeout(() => setIsFocused(false), 150)}
/>
{searching ? (
<Spinner width={24} height={24} />
) : (
<Icon name="search" size={24} onClick={() => navigate("/search")} />
)}
{search && !searching && (
{search && !searching && isFocused && (
<div
className="absolute top-full mt-2 w-full border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-black shadow-lg rounded-lg z-10 overflow-hidden"
ref={resultListRef}>

View File

@ -166,7 +166,7 @@ export default function SendSats(props: SendSatsProps) {
<ProfileImage
pubkey={v.value}
showUsername={false}
showFollowingMark={false}
showFollowDistance={false}
imageOverlay={formatShort(Math.floor((amount?.amount ?? 0) * (v.weight / total)))}
/>
))}

View File

@ -8,6 +8,7 @@ import ProfileImage from "./ProfileImage";
import { UserWebsiteLink } from "./UserWebsiteLink";
import Text from "Element/Text";
import { useEffect, useState } from "react";
import useLogin from "../../Hooks/useLogin";
interface RectElement {
getBoundingClientRect(): {
@ -35,6 +36,7 @@ export function ProfileCard({
}) {
const [showProfileMenu, setShowProfileMenu] = useState(false);
const [t, setT] = useState<ReturnType<typeof setTimeout>>();
const { publicKey: myPublicKey } = useLogin(s => ({ publicKey: s.publicKey }));
useEffect(() => {
if (show) {
@ -60,14 +62,14 @@ export function ProfileCard({
align="end">
<div className="flex flex-col g8">
<div className="flex justify-between">
<ProfileImage pubkey={""} profile={user} showProfileCard={false} link="" />
<ProfileImage pubkey={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} />
{myPublicKey !== pubkey && <FollowButton pubkey={pubkey} />}
</div>
</div>
<Text

View File

@ -8,11 +8,11 @@ import classNames from "classnames";
import Avatar from "Element/User/Avatar";
import Nip05 from "Element/User/Nip05";
import useLogin from "Hooks/useLogin";
import Icon from "Icons/Icon";
import DisplayName from "./DisplayName";
import { ProfileLink } from "./ProfileLink";
import { ProfileCard } from "./ProfileCard";
import SocialGraph from "../../SocialGraph/SocialGraph";
export interface ProfileImageProps {
pubkey: HexKey;
@ -27,7 +27,7 @@ export interface ProfileImageProps {
size?: number;
onClick?: (e: React.MouseEvent) => void;
imageOverlay?: ReactNode;
showFollowingMark?: boolean;
showFollowDistance?: boolean;
icons?: ReactNode;
showProfileCard?: boolean;
}
@ -45,14 +45,13 @@ export default function ProfileImage({
size,
imageOverlay,
onClick,
showFollowingMark = true,
showFollowDistance = true,
icons,
showProfileCard,
}: ProfileImageProps) {
const user = useUserProfile(profile ? "" : pubkey) ?? profile;
const nip05 = defaultNip ? defaultNip : user?.nip05;
const { follows } = useLogin();
const doesFollow = follows.item.includes(pubkey);
const followDistance = SocialGraph.getFollowDistance(pubkey);
const [ref, hovering] = useHover<HTMLDivElement>();
function handleClick(e: React.MouseEvent) {
@ -63,6 +62,12 @@ export default function ProfileImage({
}
function inner() {
let followDistanceColor = "";
if (followDistance <= 1) {
followDistanceColor = "success";
} else if (followDistance === 2 && SocialGraph.followedByFriendsCount(pubkey) >= 10) {
followDistanceColor = "text-nostr-orange";
}
return (
<>
<div className="avatar-wrapper" ref={ref}>
@ -72,12 +77,12 @@ export default function ProfileImage({
size={size}
imageOverlay={imageOverlay}
icons={
(doesFollow && showFollowingMark) || icons ? (
(followDistance <= 2 && showFollowDistance) || icons ? (
<>
{icons}
{showFollowingMark && (
{showFollowDistance && (
<div className="icon-circle">
<Icon name="check" className="success" size={10} />
<Icon name="check" className={followDistanceColor} size={10} />
</div>
)}
</>