Merge pull request 'Modal quick keys, profile picture & banner modal' (#640) from mmalmi/snort:main into main
Reviewed-on: #640
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import "./SpotlightMedia.css";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import Modal from "Element/Modal";
|
||||
import Icon from "Icons/Icon";
|
||||
import { ProxyImg } from "Element/ProxyImg";
|
||||
@ -17,6 +17,24 @@ export function SpotlightMedia(props: SpotlightMediaProps) {
|
||||
return props.images.at(idx % props.images.length);
|
||||
}, [idx, props]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
switch (e.key) {
|
||||
case "ArrowLeft":
|
||||
case "ArrowUp":
|
||||
dec();
|
||||
break;
|
||||
case "ArrowRight":
|
||||
case "ArrowDown":
|
||||
inc();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, []);
|
||||
|
||||
function dec() {
|
||||
setIdx(s => {
|
||||
if (s - 1 === -1) {
|
||||
@ -36,6 +54,7 @@ export function SpotlightMedia(props: SpotlightMediaProps) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="spotlight">
|
||||
<ProxyImg src={image} />
|
||||
|
@ -4,14 +4,25 @@ import { ReactNode, useEffect } from "react";
|
||||
export interface ModalProps {
|
||||
id: string;
|
||||
className?: string;
|
||||
onClose?: (e: React.MouseEvent) => void;
|
||||
onClose?: (e: React.MouseEvent | KeyboardEvent) => void;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export default function Modal(props: ModalProps) {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape" && props.onClose) {
|
||||
props.onClose(e);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.add("scroll-lock");
|
||||
return () => document.body.classList.remove("scroll-lock");
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
|
||||
return () => {
|
||||
document.body.classList.remove("scroll-lock");
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
@ -15,9 +15,10 @@ interface AvatarProps {
|
||||
image?: string;
|
||||
imageOverlay?: ReactNode;
|
||||
icons?: ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Avatar = ({ pubkey, user, size, onClick, image, imageOverlay, icons }: AvatarProps) => {
|
||||
const Avatar = ({ pubkey, user, size, onClick, image, imageOverlay, icons, className }: AvatarProps) => {
|
||||
const [url, setUrl] = useState("");
|
||||
const { proxy } = useImgProxy();
|
||||
|
||||
@ -43,7 +44,7 @@ const Avatar = ({ pubkey, user, size, onClick, image, imageOverlay, icons }: Ava
|
||||
<div
|
||||
onClick={onClick}
|
||||
style={style}
|
||||
className={`avatar${imageOverlay ? " with-overlay" : ""}`}
|
||||
className={`avatar${imageOverlay ? " with-overlay" : ""} ${className ?? ""}`}
|
||||
data-domain={domain?.toLowerCase()}
|
||||
title={getDisplayName(user, "")}>
|
||||
{icons && <div className="icons">{icons}</div>}
|
||||
|
@ -58,6 +58,7 @@ import { ZapTarget } from "Zapper";
|
||||
import { useStatusFeed } from "Feed/StatusFeed";
|
||||
|
||||
import messages from "./messages";
|
||||
import { SpotlightMediaModal } from "../Element/Deck/SpotlightMedia";
|
||||
|
||||
const NOTES = 0;
|
||||
const REACTIONS = 1;
|
||||
@ -120,6 +121,7 @@ export default function ProfilePage() {
|
||||
const isMe = loginPubKey === id;
|
||||
const [showLnQr, setShowLnQr] = useState<boolean>(false);
|
||||
const [showProfileQr, setShowProfileQr] = useState<boolean>(false);
|
||||
const [modalImage, setModalImage] = useState<string>("");
|
||||
const aboutText = user?.about || "";
|
||||
const npub = !id?.startsWith(NostrPrefix.PublicKey) ? hexToBech32(NostrPrefix.PublicKey, id || undefined) : id;
|
||||
|
||||
@ -428,7 +430,7 @@ export default function ProfilePage() {
|
||||
function avatar() {
|
||||
return (
|
||||
<div className="avatar-wrapper w-max">
|
||||
<Avatar pubkey={id ?? ""} user={user} />
|
||||
<Avatar pubkey={id ?? ""} user={user} onClick={() => setModalImage(user?.picture || "")} className="pointer" />
|
||||
<div className="profile-actions">
|
||||
{renderIcons()}
|
||||
{!isMe && id && <FollowButton className="primary" pubkey={id} />}
|
||||
@ -506,7 +508,15 @@ export default function ProfilePage() {
|
||||
return (
|
||||
<>
|
||||
<div className="profile">
|
||||
{user?.banner && <ProxyImg alt="banner" className="banner" src={user.banner} size={w} />}
|
||||
{user?.banner && (
|
||||
<ProxyImg
|
||||
alt="banner"
|
||||
className="banner pointer"
|
||||
src={user.banner}
|
||||
size={w}
|
||||
onClick={() => setModalImage(user.banner || "")}
|
||||
/>
|
||||
)}
|
||||
<div className="profile-wrapper w-max">
|
||||
{avatar()}
|
||||
{userDetails()}
|
||||
@ -520,6 +530,7 @@ export default function ProfilePage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="main-content">{tabContent()}</div>
|
||||
{modalImage && <SpotlightMediaModal onClose={() => setModalImage("")} images={[modalImage]} idx={0} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user