Snort deck improvements
This commit is contained in:
parent
5b9f4b0b8d
commit
1eb7216177
@ -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} />;
|
||||||
})}
|
})}
|
||||||
|
@ -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}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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
|
||||||
|
@ -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)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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()}`)}
|
/>
|
||||||
/>
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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} />;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user