Snort deck improvements

This commit is contained in:
Kieran 2023-09-25 13:59:31 +01:00
parent 5b9f4b0b8d
commit 1eb7216177
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
12 changed files with 72 additions and 53 deletions

View File

@ -6,7 +6,7 @@ export default function BlockList() {
const { blocked } = useModeration(); const { blocked } = useModeration();
return ( return (
<div className="main-content"> <div className="main-content p">
{blocked.map(a => { {blocked.map(a => {
return <ProfilePreview actions={<BlockButton pubkey={a} />} pubkey={a} options={{ about: false }} key={a} />; return <ProfilePreview actions={<BlockButton pubkey={a} />} pubkey={a} options={{ about: false }} key={a} />;
})} })}

View File

@ -28,7 +28,7 @@ const Bookmarks = ({ pubkey, bookmarks, related }: BookmarksProps) => {
return ( return (
<div className="main-content"> <div className="main-content">
<div className="mb10 flex-end"> <div className="flex-end p">
<select <select
disabled={ps.length <= 1} disabled={ps.length <= 1}
value={onlyPubkey} value={onlyPubkey}

View File

@ -4,7 +4,7 @@ import { ReactNode, useEffect } from "react";
export interface ModalProps { export interface ModalProps {
id: string; id: string;
className?: string; className?: string;
onClose?: () => void; onClose?: (e: React.MouseEvent) => void;
children: ReactNode; children: ReactNode;
} }
@ -16,8 +16,8 @@ export default function Modal(props: ModalProps) {
return ( return (
<div className={`modal${props.className ? ` ${props.className}` : ""}`} onClick={props.onClose}> <div className={`modal${props.className ? ` ${props.className}` : ""}`} onClick={props.onClose}>
<div className="modal-body" onClick={e => e.stopPropagation()}> <div className="modal-body" onClick={props.onClose}>
{props.children} <div onClick={e => e.stopPropagation()}>{props.children}</div>
</div> </div>
</div> </div>
); );

View File

@ -15,9 +15,9 @@ export default function MutedList({ pubkeys }: MutedListProps) {
const hasAllMuted = pubkeys.every(isMuted); const hasAllMuted = pubkeys.every(isMuted);
return ( return (
<div className="main-content"> <div className="main-content p">
<div className="flex mt10"> <div className="flex f-space">
<div className="f-grow bold"> <div className="bold">
<FormattedMessage {...messages.MuteCount} values={{ n: pubkeys?.length }} /> <FormattedMessage {...messages.MuteCount} values={{ n: pubkeys?.length }} />
</div> </div>
<button <button

View File

@ -61,6 +61,7 @@ export interface NoteProps {
canUnpin?: boolean; canUnpin?: boolean;
canUnbookmark?: boolean; canUnbookmark?: boolean;
canClick?: boolean; canClick?: boolean;
showMediaSpotlight?: boolean;
}; };
} }
@ -210,6 +211,7 @@ export function NoteInner(props: NoteProps) {
depth={props.depth} depth={props.depth}
truncate={255} truncate={255}
disableLinkPreview={true} disableLinkPreview={true}
disableMediaSpotlight={!(props.options?.showMediaSpotlight ?? true)}
/> />
{image && <ProxyImg src={image} />} {image && <ProxyImg src={image} />}
</div> </div>
@ -225,6 +227,7 @@ export function NoteInner(props: NoteProps) {
creator={ev.pubkey} creator={ev.pubkey}
depth={props.depth} depth={props.depth}
disableMedia={!(options.showMedia ?? true)} disableMedia={!(options.showMedia ?? true)}
disableMediaSpotlight={!(props.options?.showMediaSpotlight ?? true)}
/> />
); );
} }

View File

@ -2,7 +2,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
min-height: 59px; min-height: 59px;
padding: 0 16px;
} }
.profile-preview .pfp { .profile-preview .pfp {

View File

@ -216,7 +216,7 @@ export function ThreadRoute() {
); );
} }
export function Thread(props: { onBack?: () => void }) { export function Thread(props: { onBack?: () => void; disableSpotlight?: boolean }) {
const thread = useContext(ThreadContext); const thread = useContext(ThreadContext);
const navigate = useNavigate(); const navigate = useNavigate();
@ -248,7 +248,7 @@ export function Thread(props: { onBack?: () => void }) {
key={note.id} key={note.id}
data={note} data={note}
related={getReactions(thread.reactions, note.id)} related={getReactions(thread.reactions, note.id)}
options={{ showReactionsLink: true }} options={{ showReactionsLink: true, showMediaSpotlight: !props.disableSpotlight }}
onClick={navigateThread} onClick={navigateThread}
/> />
); );

View File

@ -16,20 +16,16 @@ const Zap = ({ zap, showZapped = true }: { zap: ParsedZap; showZapped?: boolean
const pubKey = useLogin().publicKey; const pubKey = useLogin().publicKey;
return valid && sender ? ( return valid && sender ? (
<div className="zap note card"> <div className="card">
<div className="header"> <div className="flex f-space">
<ProfileImage pubkey={sender} /> <ProfileImage pubkey={sender} />
{receiver !== pubKey && showZapped && <ProfileImage pubkey={unwrap(receiver)} />} {receiver !== pubKey && showZapped && <ProfileImage pubkey={unwrap(receiver)} />}
<div className="amount"> <h3>
<span className="amount-number"> <FormattedMessage {...messages.Sats} values={{ n: formatShort(amount ?? 0) }} />
<FormattedMessage {...messages.Sats} values={{ n: formatShort(amount ?? 0) }} /> </h3>
</span>
</div>
</div> </div>
{(content?.length ?? 0) > 0 && sender && ( {(content?.length ?? 0) > 0 && sender && (
<div className="body"> <Text id={zap.id} creator={sender} content={unwrap(content)} tags={[]} />
<Text id={zap.id} creator={sender} content={unwrap(content)} tags={[]} />
</div>
)} )}
</div> </div>
) : null; ) : null;

View File

@ -52,20 +52,23 @@
cursor: pointer; cursor: pointer;
} }
.thread-overlay .modal-body { .modal.thread-overlay > .modal-body {
background-color: unset; background-color: unset;
padding: 0; padding: 0;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
--border-color: #3a3a3a;
}
.modal.thread-overlay > .modal-body > div {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
border-radius: unset; border-radius: unset;
justify-content: center; justify-content: center;
gap: 16px; gap: 16px;
--border-color: #3a3a3a;
} }
.thread-overlay .modal-body > div:last-of-type { .modal.thread-overlay > .modal-body > div > div:last-of-type {
width: 550px; width: 550px;
min-width: 550px; min-width: 550px;
height: 100vh; height: 100vh;

View File

@ -64,7 +64,7 @@ export function SnortDeckLayout() {
case "articles": case "articles":
return <ArticlesCol />; return <ArticlesCol />;
case "notifications": case "notifications":
return <NotificationsCol />; return <NotificationsCol setThread={deckScope.setThread} />;
} }
})} })}
</div> </div>
@ -74,7 +74,7 @@ export function SnortDeckLayout() {
<ThreadContextWrapper link={deckScope.thread}> <ThreadContextWrapper link={deckScope.thread}>
<SpotlightFromThread onClose={() => deckScope.setThread(undefined)} /> <SpotlightFromThread onClose={() => deckScope.setThread(undefined)} />
<div> <div>
<Thread onBack={() => deckScope.setThread(undefined)} /> <Thread onBack={() => deckScope.setThread(undefined)} disableSpotlight={true} />
</div> </div>
</ThreadContextWrapper> </ThreadContextWrapper>
</Modal> </Modal>
@ -167,7 +167,7 @@ function MediaCol({ setThread }: { setThread: (e: NostrLink) => void }) {
); );
} }
function NotificationsCol() { function NotificationsCol({ setThread }: { setThread: (e: NostrLink) => void }) {
return ( return (
<div> <div>
<div className="deck-col-header flex g8"> <div className="deck-col-header flex g8">
@ -175,7 +175,7 @@ function NotificationsCol() {
<FormattedMessage defaultMessage="Notifications" /> <FormattedMessage defaultMessage="Notifications" />
</div> </div>
<div> <div>
<NotificationsPage /> <NotificationsPage onClick={setThread} />
</div> </div>
</div> </div>
); );

View File

@ -17,6 +17,8 @@ import useModeration from "Hooks/useModeration";
import { useEventFeed } from "Feed/EventFeed"; import { useEventFeed } from "Feed/EventFeed";
import Text from "Element/Text"; import Text from "Element/Text";
import { formatShort } from "Number"; import { formatShort } from "Number";
import { LiveEvent } from "Element/LiveEvent";
import ProfilePreview from "Element/ProfilePreview";
function notificationContext(ev: TaggedNostrEvent) { function notificationContext(ev: TaggedNostrEvent) {
switch (ev.kind) { switch (ev.kind) {
@ -55,7 +57,7 @@ function notificationContext(ev: TaggedNostrEvent) {
} }
} }
export default function NotificationsPage() { export default function NotificationsPage({ onClick }: { onClick?: (link: NostrLink) => void }) {
const login = useLogin(); const login = useLogin();
const { isMuted } = useModeration(); const { isMuted } = useModeration();
const groupInterval = 3600 * 3; const groupInterval = 3600 * 3;
@ -90,15 +92,17 @@ export default function NotificationsPage() {
return ( return (
<div className="main-content"> <div className="main-content">
{login.publicKey && [...timeGrouped.entries()].map(([k, g]) => <NotificationGroup key={k} evs={g} />)} {login.publicKey &&
[...timeGrouped.entries()].map(([k, g]) => <NotificationGroup key={k} evs={g} onClick={onClick} />)}
</div> </div>
); );
} }
function NotificationGroup({ evs }: { evs: Array<TaggedNostrEvent> }) { function NotificationGroup({ evs, onClick }: { evs: Array<TaggedNostrEvent>; onClick?: (link: NostrLink) => void }) {
const { ref, inView } = useInView({ triggerOnce: true }); const { ref, inView } = useInView({ triggerOnce: true });
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const kind = evs[0].kind; const kind = evs[0].kind;
const navigate = useNavigate();
const zaps = useMemo(() => { const zaps = useMemo(() => {
return evs.filter(a => a.kind === EventKind.ZapReceipt).map(a => parseZap(a, UserCache)); return evs.filter(a => a.kind === EventKind.ZapReceipt).map(a => parseZap(a, UserCache));
@ -183,7 +187,7 @@ function NotificationGroup({ evs }: { evs: Array<TaggedNostrEvent> }) {
</div> </div>
<div>{kind === EventKind.ZapReceipt && formatShort(totalZaps)}</div> <div>{kind === EventKind.ZapReceipt && formatShort(totalZaps)}</div>
</div> </div>
<div className="flex-column g12"> <div className="flex-column w-max g12">
<div className="flex"> <div className="flex">
{pubkeys {pubkeys
.filter(a => a !== "anon") .filter(a => a !== "anon")
@ -208,7 +212,18 @@ function NotificationGroup({ evs }: { evs: Array<TaggedNostrEvent> }) {
)} )}
</div> </div>
)} )}
{context && <NotificationContext link={context} />} {context && (
<NotificationContext
link={context}
onClick={() => {
if (onClick) {
onClick(context);
} else {
navigate(`/e/${context.encode()}`);
}
}}
/>
)}
</div> </div>
</> </>
)} )}
@ -216,22 +231,25 @@ function NotificationGroup({ evs }: { evs: Array<TaggedNostrEvent> }) {
); );
} }
function NotificationContext({ link }: { link: NostrLink }) { function NotificationContext({ link, onClick }: { link: NostrLink; onClick: () => void }) {
const { data: ev } = useEventFeed(link); const { data: ev } = useEventFeed(link);
const navigate = useNavigate(); if (link.type === NostrPrefix.PublicKey) {
return <ProfilePreview pubkey={link.id} actions={<></>} />;
}
if (!ev) return;
if (ev.kind === EventKind.LiveEvent) {
return <LiveEvent ev={ev} />;
}
return ( return (
ev && ( <Text
<Text id={ev.id}
id={ev.id} content={ev.content}
content={ev.content} tags={ev.tags}
tags={ev.tags} creator={ev.pubkey}
creator={ev.pubkey} truncate={120}
truncate={120} disableLinkPreview={true}
disableLinkPreview={true} className="content"
className="content" onClick={onClick}
onClick={() => navigate(`/${link.encode()}`)} />
/>
)
); );
} }

View File

@ -74,9 +74,9 @@ function ZapsProfileTab({ id }: { id: HexKey }) {
const zapsTotal = zaps.reduce((acc, z) => acc + z.amount, 0); const zapsTotal = zaps.reduce((acc, z) => acc + z.amount, 0);
return ( return (
<div className="main-content"> <div className="main-content">
<div className="zaps-total"> <h2 className="p">
<FormattedMessage {...messages.Sats} values={{ n: formatShort(zapsTotal) }} /> <FormattedMessage {...messages.Sats} values={{ n: formatShort(zapsTotal) }} />
</div> </h2>
{zaps.map(z => ( {zaps.map(z => (
<ZapElement showZapped={false} zap={z} /> <ZapElement showZapped={false} zap={z} />
))} ))}
@ -86,12 +86,12 @@ function ZapsProfileTab({ id }: { id: HexKey }) {
function FollowersTab({ id }: { id: HexKey }) { function FollowersTab({ id }: { id: HexKey }) {
const followers = useFollowersFeed(id); const followers = useFollowersFeed(id);
return <FollowsList pubkeys={followers} showAbout={true} />; return <FollowsList pubkeys={followers} showAbout={true} className="p" />;
} }
function FollowsTab({ id }: { id: HexKey }) { function FollowsTab({ id }: { id: HexKey }) {
const follows = useFollowsFeed(id); const follows = useFollowsFeed(id);
return <FollowsList pubkeys={follows} showAbout={true} />; return <FollowsList pubkeys={follows} showAbout={true} className="p" />;
} }
function RelaysTab({ id }: { id: HexKey }) { function RelaysTab({ id }: { id: HexKey }) {
@ -402,7 +402,7 @@ export default function ProfilePage() {
} }
case FOLLOWS: { case FOLLOWS: {
if (isMe) { if (isMe) {
return <FollowsList pubkeys={follows} showFollowAll={!isMe} showAbout={false} />; return <FollowsList pubkeys={follows} showFollowAll={!isMe} showAbout={false} className="p" />;
} else { } else {
return <FollowsTab id={id} />; return <FollowsTab id={id} />;
} }