dont blur images in shared posts

This commit is contained in:
hzrd149 2023-05-25 11:16:19 -05:00
parent 40c5e19158
commit 2d2e2332b4
13 changed files with 210 additions and 189 deletions

@ -0,0 +1,5 @@
---
"nostrudel": patch
---
Dont blur images on shared notes

@ -1,51 +0,0 @@
import { Link as RouterLink } from "react-router-dom";
import moment from "moment";
import { Card, CardBody, CardHeader, Flex, Heading, Link } from "@chakra-ui/react";
import { useIsMobile } from "../hooks/use-is-mobile";
import { NoteContents } from "./note/note-contents";
import { useUserContacts } from "../hooks/use-user-contacts";
import { useCurrentAccount } from "../hooks/use-current-account";
import { NostrEvent } from "../types/nostr-event";
import { UserAvatarLink } from "./user-avatar-link";
import { UserLink } from "./user-link";
import { UserDnsIdentityIcon } from "./user-dns-identity";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip19";
import { convertTimestampToDate } from "../helpers/date";
import useSubject from "../hooks/use-subject";
import appSettings from "../services/app-settings";
import EventVerificationIcon from "./event-verification-icon";
import { useReadRelayUrls } from "../hooks/use-client-relays";
const EmbeddedNote = ({ note }: { note: NostrEvent }) => {
const account = useCurrentAccount();
const { showSignatureVerification } = useSubject(appSettings);
const readRelays = useReadRelayUrls();
const contacts = useUserContacts(account.pubkey, readRelays);
const following = contacts?.contacts || [];
return (
<Card variant="outline">
<CardHeader padding="2">
<Flex flex="1" gap="2" alignItems="center" wrap="wrap">
<UserAvatarLink pubkey={note.pubkey} size="xs" />
<Heading size="sm" display="inline">
<UserLink pubkey={note.pubkey} />
</Heading>
<UserDnsIdentityIcon pubkey={note.pubkey} onlyIcon />
<Flex grow={1} />
{showSignatureVerification && <EventVerificationIcon event={note} />}
<Link as={RouterLink} to={`/n/${normalizeToBech32(note.id, Bech32Prefix.Note)}`} whiteSpace="nowrap">
{moment(convertTimestampToDate(note.created_at)).fromNow()}
</Link>
</Flex>
</CardHeader>
<CardBody p="0">
<NoteContents event={note} trusted={following.includes(note.pubkey)} maxHeight={200} />
</CardBody>
</Card>
);
};
export default EmbeddedNote;

@ -0,0 +1,43 @@
import { Link as RouterLink } from "react-router-dom";
import moment from "moment";
import { Card, CardBody, CardHeader, Flex, Heading, Link } from "@chakra-ui/react";
import { NoteContents } from "./note-contents";
import { NostrEvent } from "../../types/nostr-event";
import { UserAvatarLink } from "../user-avatar-link";
import { UserLink } from "../user-link";
import { UserDnsIdentityIcon } from "../user-dns-identity";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
import { convertTimestampToDate } from "../../helpers/date";
import useSubject from "../../hooks/use-subject";
import appSettings from "../../services/app-settings";
import EventVerificationIcon from "../event-verification-icon";
import { TrustProvider } from "./trust";
export default function EmbeddedNote({ note }: { note: NostrEvent }) {
const { showSignatureVerification } = useSubject(appSettings);
return (
<TrustProvider event={note}>
<Card variant="outline">
<CardHeader padding="2">
<Flex flex="1" gap="2" alignItems="center" wrap="wrap">
<UserAvatarLink pubkey={note.pubkey} size="xs" />
<Heading size="sm" display="inline">
<UserLink pubkey={note.pubkey} />
</Heading>
<UserDnsIdentityIcon pubkey={note.pubkey} onlyIcon />
<Flex grow={1} />
{showSignatureVerification && <EventVerificationIcon event={note} />}
<Link as={RouterLink} to={`/n/${normalizeToBech32(note.id, Bech32Prefix.Note)}`} whiteSpace="nowrap">
{moment(convertTimestampToDate(note.created_at)).fromNow()}
</Link>
</Flex>
</CardHeader>
<CardBody p="0">
<NoteContents event={note} maxHeight={200} />
</CardBody>
</Card>
</TrustProvider>
);
}

@ -2,12 +2,7 @@ import React, { useMemo } from "react";
import { Link as RouterLink } from "react-router-dom";
import moment from "moment";
import {
Alert,
AlertDescription,
AlertIcon,
AlertTitle,
Box,
Button,
ButtonGroup,
Card,
CardBody,
@ -18,57 +13,29 @@ import {
Heading,
IconButton,
Link,
Spacer,
} from "@chakra-ui/react";
import { NostrEvent } from "../../types/nostr-event";
import { UserAvatarLink } from "../user-avatar-link";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
import { NoteContents } from "./note-contents";
import { NoteMenu } from "./note-menu";
import { useUserContacts } from "../../hooks/use-user-contacts";
import { NoteRelays } from "./note-relays";
import { useIsMobile } from "../../hooks/use-is-mobile";
import { UserLink } from "../user-link";
import { UserDnsIdentityIcon } from "../user-dns-identity";
import { convertTimestampToDate } from "../../helpers/date";
import { useCurrentAccount } from "../../hooks/use-current-account";
import ReactionButton from "./buttons/reaction-button";
import NoteZapButton from "./note-zap-button";
import { ExpandProvider, useExpand } from "./expanded";
import { ExpandProvider } from "./expanded";
import useSubject from "../../hooks/use-subject";
import appSettings from "../../services/app-settings";
import EventVerificationIcon from "../event-verification-icon";
import { ReplyButton } from "./buttons/reply-button";
import { RepostButton } from "./buttons/repost-button";
import { QuoteRepostButton } from "./buttons/quote-repost-button";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { ExternalLinkIcon } from "../icons";
import SensitiveContentWarning from "../sensitive-content-warning";
import useAppSettings from "../../hooks/use-app-settings";
function NoteContentWithWarning({ event, maxHeight }: { event: NostrEvent; maxHeight?: number }) {
const account = useCurrentAccount();
const expand = useExpand();
const settings = useAppSettings();
const readRelays = useReadRelayUrls();
const contacts = useUserContacts(account.pubkey, readRelays);
const following = contacts?.contacts || [];
const contentWarning = event.tags.find((t) => t[0] === "content-warning")?.[1];
const showContentWarning = settings.showContentWarning && contentWarning && !expand?.expanded;
return showContentWarning ? (
<SensitiveContentWarning description={contentWarning} />
) : (
<NoteContents
event={event}
trusted={event.pubkey === account.pubkey || following.includes(event.pubkey)}
maxHeight={maxHeight}
/>
);
}
import NoteContentWithWarning from "./note-content-with-warning";
import { TrustProvider } from "./trust";
export type NoteProps = {
event: NostrEvent;
@ -83,50 +50,52 @@ export const Note = React.memo(({ event, maxHeight, variant = "outline" }: NoteP
const externalLink = useMemo(() => event.tags.find((t) => t[0] === "mostr"), [event]);
return (
<ExpandProvider>
<Card variant={variant}>
<CardHeader padding="2">
<Flex flex="1" gap="2" alignItems="center" wrap="wrap">
<UserAvatarLink pubkey={event.pubkey} size={isMobile ? "xs" : "sm"} />
<TrustProvider event={event}>
<ExpandProvider>
<Card variant={variant}>
<CardHeader padding="2">
<Flex flex="1" gap="2" alignItems="center" wrap="wrap">
<UserAvatarLink pubkey={event.pubkey} size={isMobile ? "xs" : "sm"} />
<Heading size="sm" display="inline">
<UserLink pubkey={event.pubkey} />
</Heading>
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<Flex grow={1} />
{showSignatureVerification && <EventVerificationIcon event={event} />}
<Link as={RouterLink} to={`/n/${normalizeToBech32(event.id, Bech32Prefix.Note)}`} whiteSpace="nowrap">
{moment(convertTimestampToDate(event.created_at)).fromNow()}
</Link>
</Flex>
</CardHeader>
<CardBody p="0">
<NoteContentWithWarning event={event} maxHeight={maxHeight} />
</CardBody>
<CardFooter padding="2" display="flex" gap="2">
<ButtonGroup size="sm" variant="link">
<ReplyButton event={event} />
<RepostButton event={event} />
<QuoteRepostButton event={event} />
<NoteZapButton note={event} size="sm" />
{showReactions && <ReactionButton note={event} size="sm" />}
</ButtonGroup>
<Box flexGrow={1} />
{externalLink && (
<IconButton
as={Link}
icon={<ExternalLinkIcon />}
aria-label="Open External"
href={externalLink[1]}
size="sm"
variant="link"
target="_blank"
/>
)}
<NoteRelays event={event} size="sm" variant="link" />
<NoteMenu event={event} size="sm" variant="link" aria-label="More Options" />
</CardFooter>
</Card>
</ExpandProvider>
<Heading size="sm" display="inline">
<UserLink pubkey={event.pubkey} />
</Heading>
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<Flex grow={1} />
{showSignatureVerification && <EventVerificationIcon event={event} />}
<Link as={RouterLink} to={`/n/${normalizeToBech32(event.id, Bech32Prefix.Note)}`} whiteSpace="nowrap">
{moment(convertTimestampToDate(event.created_at)).fromNow()}
</Link>
</Flex>
</CardHeader>
<CardBody p="0">
<NoteContentWithWarning event={event} maxHeight={maxHeight} />
</CardBody>
<CardFooter padding="2" display="flex" gap="2">
<ButtonGroup size="sm" variant="link">
<ReplyButton event={event} />
<RepostButton event={event} />
<QuoteRepostButton event={event} />
<NoteZapButton note={event} size="sm" />
{showReactions && <ReactionButton note={event} size="sm" />}
</ButtonGroup>
<Box flexGrow={1} />
{externalLink && (
<IconButton
as={Link}
icon={<ExternalLinkIcon />}
aria-label="Open External"
href={externalLink[1]}
size="sm"
variant="link"
target="_blank"
/>
)}
<NoteRelays event={event} size="sm" variant="link" />
<NoteMenu event={event} size="sm" variant="link" aria-label="More Options" />
</CardFooter>
</Card>
</ExpandProvider>
</TrustProvider>
);
});

@ -0,0 +1,20 @@
import { NostrEvent } from "../../types/nostr-event";
import { NoteContents } from "./note-contents";
import { useExpand } from "./expanded";
import SensitiveContentWarning from "../sensitive-content-warning";
import useAppSettings from "../../hooks/use-app-settings";
export default function NoteContentWithWarning({ event, maxHeight }: { event: NostrEvent; maxHeight?: number }) {
const expand = useExpand();
const settings = useAppSettings();
const contentWarning = event.tags.find((t) => t[0] === "content-warning")?.[1];
const showContentWarning = settings.showContentWarning && contentWarning && !expand?.expanded;
return showContentWarning ? (
<SensitiveContentWarning description={contentWarning} />
) : (
<NoteContents event={event} maxHeight={maxHeight} />
);
}

@ -21,8 +21,9 @@ import {
embedNostrHashtags,
} from "../embed-types";
import { ImageGalleryProvider } from "../image-gallery";
import { useTrusted } from "./trust";
function buildContents(event: NostrEvent | DraftNostrEvent, trusted: boolean = false) {
function buildContents(event: NostrEvent | DraftNostrEvent, trusted = false) {
let content: EmbedableContent = [event.content.trim()];
content = embedLightningInvoice(content);
@ -59,12 +60,12 @@ const GradientOverlay = styled.div`
export type NoteContentsProps = {
event: NostrEvent | DraftNostrEvent;
trusted?: boolean;
maxHeight?: number;
};
export const NoteContents = React.memo(({ event, trusted, maxHeight }: NoteContentsProps) => {
const content = buildContents(event, trusted ?? false);
export const NoteContents = React.memo(({ event, maxHeight }: NoteContentsProps) => {
const trusted = useTrusted();
const content = buildContents(event, trusted);
const expand = useExpand();
const [innerHeight, setInnerHeight] = useState(0);
const ref = useRef<HTMLDivElement | null>(null);

@ -1,6 +1,6 @@
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import useSingleEvent from "../../hooks/use-single-event";
import EmbeddedNote from "../embeded-note";
import EmbeddedNote from "./embeded-note";
import { NoteLink } from "../note-link";
const QuoteNote = ({ noteId, relay }: { noteId: string; relay?: string }) => {

@ -0,0 +1,53 @@
import { Box, Flex, Heading, SkeletonText } from "@chakra-ui/react";
import { useAsync } from "react-use";
import clientRelaysService from "../../services/client-relays";
import singleEventService from "../../services/single-event";
import { isETag, NostrEvent } from "../../types/nostr-event";
import { ErrorFallback } from "../error-boundary";
import { Note } from ".";
import { NoteMenu } from "./note-menu";
import { UserAvatar } from "../user-avatar";
import { UserDnsIdentityIcon } from "../user-dns-identity";
import { UserLink } from "../user-link";
import { unique } from "../../helpers/array";
import { TrustProvider } from "./trust";
export default function RepostNote({ event, maxHeight }: { event: NostrEvent; maxHeight?: number }) {
const {
value: repostNote,
loading,
error,
} = useAsync(async () => {
const [_, eventId, relay] = event.tags.find(isETag) ?? [];
if (eventId) {
const readRelays = clientRelaysService.getReadUrls();
if (relay) readRelays.push(relay);
return singleEventService.requestEvent(eventId, unique(readRelays));
}
return null;
}, [event]);
return (
<TrustProvider event={event}>
<Flex gap="2" direction="column">
<Flex gap="2" alignItems="center" pl="1">
<UserAvatar pubkey={event.pubkey} size="xs" />
<Heading size="sm" display="inline">
<UserLink pubkey={event.pubkey} />
</Heading>
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<span>Shared note</span>
<Box flex={1} />
<NoteMenu event={event} size="sm" variant="link" aria-label="note options" />
</Flex>
{loading ? (
<SkeletonText />
) : repostNote ? (
<Note event={repostNote} maxHeight={maxHeight} />
) : (
<ErrorFallback error={error} />
)}
</Flex>
</TrustProvider>
);
}

@ -0,0 +1,28 @@
import React, { PropsWithChildren, useContext } from "react";
import { NostrEvent } from "../../types/nostr-event";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useUserContacts } from "../../hooks/use-user-contacts";
import { useCurrentAccount } from "../../hooks/use-current-account";
const TrustContext = React.createContext<boolean>(false);
export function useTrusted() {
return useContext(TrustContext);
}
export function TrustProvider({
children,
event,
trust = false,
}: PropsWithChildren & { event?: NostrEvent; trust?: boolean }) {
const parentTrust = useContext(TrustContext);
const account = useCurrentAccount();
const readRelays = useReadRelayUrls();
const contacts = useUserContacts(account.pubkey, readRelays);
const following = contacts?.contacts || [];
const isEventTrusted = trust || (!!event && (event.pubkey === account.pubkey || following.includes(event.pubkey)));
return <TrustContext.Provider value={parentTrust || isEventTrusted}>{children}</TrustContext.Provider>;
}

@ -27,6 +27,7 @@ import { ImageIcon } from "../icons";
import { NoteLink } from "../note-link";
import { NoteContents } from "../note/note-contents";
import { PostResults } from "./post-results";
import { TrustProvider } from "../note/trust";
function emptyDraft(): DraftNostrEvent {
return {
@ -139,7 +140,9 @@ export const PostModal = ({ isOpen, onClose, initialDraft }: PostModalProps) =>
</Text>
)}
{showPreview ? (
<NoteContents event={finalizeNote(draft)} trusted />
<TrustProvider trust>
<NoteContents event={finalizeNote(draft)} />
</TrustProvider>
) : (
<Textarea
autoFocus

@ -1,50 +0,0 @@
import { Box, Flex, Heading, SkeletonText } from "@chakra-ui/react";
import { useAsync } from "react-use";
import clientRelaysService from "../services/client-relays";
import singleEventService from "../services/single-event";
import { isETag, NostrEvent } from "../types/nostr-event";
import { ErrorFallback } from "./error-boundary";
import { Note } from "./note";
import { NoteMenu } from "./note/note-menu";
import { UserAvatar } from "./user-avatar";
import { UserDnsIdentityIcon } from "./user-dns-identity";
import { UserLink } from "./user-link";
import { unique } from "../helpers/array";
export default function RepostNote({ event, maxHeight }: { event: NostrEvent; maxHeight?: number }) {
const {
value: repostNote,
loading,
error,
} = useAsync(async () => {
const [_, eventId, relay] = event.tags.find(isETag) ?? [];
if (eventId) {
const readRelays = clientRelaysService.getReadUrls();
if (relay) readRelays.push(relay);
return singleEventService.requestEvent(eventId, unique(readRelays));
}
return null;
}, [event]);
return (
<Flex gap="2" direction="column">
<Flex gap="2" alignItems="center" pl="1">
<UserAvatar pubkey={event.pubkey} size="xs" />
<Heading size="sm" display="inline">
<UserLink pubkey={event.pubkey} />
</Heading>
<UserDnsIdentityIcon pubkey={event.pubkey} onlyIcon />
<span>Shared note</span>
<Box flex={1} />
<NoteMenu event={event} size="sm" variant="link" aria-label="note options" />
</Flex>
{loading ? (
<SkeletonText />
) : repostNote ? (
<Note event={repostNote} maxHeight={maxHeight} />
) : (
<ErrorFallback error={error} />
)}
</Flex>
);
}

@ -10,7 +10,7 @@ import { useContext } from "react";
import { PostModalContext } from "../../providers/post-modal-provider";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useCurrentAccount } from "../../hooks/use-current-account";
import RepostNote from "../../components/repost-note";
import RepostNote from "../../components/note/repost-note";
export default function FollowingTab() {
const account = useCurrentAccount();

@ -20,7 +20,7 @@ import moment from "moment";
import { useOutletContext } from "react-router-dom";
import { RelayIcon } from "../../components/icons";
import { Note } from "../../components/note";
import RepostNote from "../../components/repost-note";
import RepostNote from "../../components/note/repost-note";
import { isReply, isRepost, truncatedId } from "../../helpers/nostr-event";
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
import { useAdditionalRelayContext } from "../../providers/additional-relay-context";