mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-24 07:20:17 +02:00
Show content warning for NIP-36 notes
This commit is contained in:
parent
0df1db85ae
commit
285a2dd1c7
5
.changeset/rich-plants-explode.md
Normal file
5
.changeset/rich-plants-explode.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add content warning for NIP-36 notes
|
@ -2,7 +2,12 @@ import React, { useMemo } from "react";
|
|||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertDescription,
|
||||||
|
AlertIcon,
|
||||||
|
AlertTitle,
|
||||||
Box,
|
Box,
|
||||||
|
Button,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
Card,
|
Card,
|
||||||
CardBody,
|
CardBody,
|
||||||
@ -13,6 +18,7 @@ import {
|
|||||||
Heading,
|
Heading,
|
||||||
IconButton,
|
IconButton,
|
||||||
Link,
|
Link,
|
||||||
|
Spacer,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { NostrEvent } from "../../types/nostr-event";
|
import { NostrEvent } from "../../types/nostr-event";
|
||||||
import { UserAvatarLink } from "../user-avatar-link";
|
import { UserAvatarLink } from "../user-avatar-link";
|
||||||
@ -29,7 +35,7 @@ import { convertTimestampToDate } from "../../helpers/date";
|
|||||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||||
import ReactionButton from "./buttons/reaction-button";
|
import ReactionButton from "./buttons/reaction-button";
|
||||||
import NoteZapButton from "./note-zap-button";
|
import NoteZapButton from "./note-zap-button";
|
||||||
import { ExpandProvider } from "./expanded";
|
import { ExpandProvider, useExpand } from "./expanded";
|
||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
import appSettings from "../../services/app-settings";
|
import appSettings from "../../services/app-settings";
|
||||||
import EventVerificationIcon from "../event-verification-icon";
|
import EventVerificationIcon from "../event-verification-icon";
|
||||||
@ -38,6 +44,31 @@ import { RepostButton } from "./buttons/repost-button";
|
|||||||
import { QuoteRepostButton } from "./buttons/quote-repost-button";
|
import { QuoteRepostButton } from "./buttons/quote-repost-button";
|
||||||
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||||
import { ExternalLinkIcon } from "../icons";
|
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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export type NoteProps = {
|
export type NoteProps = {
|
||||||
event: NostrEvent;
|
event: NostrEvent;
|
||||||
@ -46,13 +77,8 @@ export type NoteProps = {
|
|||||||
};
|
};
|
||||||
export const Note = React.memo(({ event, maxHeight, variant = "outline" }: NoteProps) => {
|
export const Note = React.memo(({ event, maxHeight, variant = "outline" }: NoteProps) => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const account = useCurrentAccount();
|
|
||||||
const { showReactions, showSignatureVerification } = useSubject(appSettings);
|
const { showReactions, showSignatureVerification } = useSubject(appSettings);
|
||||||
|
|
||||||
const readRelays = useReadRelayUrls();
|
|
||||||
const contacts = useUserContacts(account.pubkey, readRelays);
|
|
||||||
const following = contacts?.contacts || [];
|
|
||||||
|
|
||||||
// find mostr external link
|
// find mostr external link
|
||||||
const externalLink = useMemo(() => event.tags.find((t) => t[0] === "mostr"), [event]);
|
const externalLink = useMemo(() => event.tags.find((t) => t[0] === "mostr"), [event]);
|
||||||
|
|
||||||
@ -75,11 +101,7 @@ export const Note = React.memo(({ event, maxHeight, variant = "outline" }: NoteP
|
|||||||
</Flex>
|
</Flex>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody px="2" py="0">
|
<CardBody px="2" py="0">
|
||||||
<NoteContents
|
<NoteContentWithWarning event={event} maxHeight={maxHeight} />
|
||||||
event={event}
|
|
||||||
trusted={event.pubkey === account.pubkey || following.includes(event.pubkey)}
|
|
||||||
maxHeight={maxHeight}
|
|
||||||
/>
|
|
||||||
</CardBody>
|
</CardBody>
|
||||||
<CardFooter padding="2" display="flex" gap="2">
|
<CardFooter padding="2" display="flex" gap="2">
|
||||||
<ButtonGroup size="sm" variant="link">
|
<ButtonGroup size="sm" variant="link">
|
||||||
|
42
src/components/sensitive-content-warning.tsx
Normal file
42
src/components/sensitive-content-warning.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { Alert, AlertDescription, AlertIcon, AlertProps, AlertTitle, Button, Spacer, useModal } from "@chakra-ui/react";
|
||||||
|
import { useIsMobile } from "../hooks/use-is-mobile";
|
||||||
|
import { useExpand } from "./note/expanded";
|
||||||
|
|
||||||
|
export default function SensitiveContentWarning({ description }: { description: string } & AlertProps) {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
const expand = useExpand();
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
status="warning"
|
||||||
|
flexDirection="column"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
textAlign="center"
|
||||||
|
height="200px"
|
||||||
|
>
|
||||||
|
<AlertIcon boxSize="40px" mr={0} />
|
||||||
|
<AlertTitle mt={4} mb={1} fontSize="lg">
|
||||||
|
Sensitive Content
|
||||||
|
</AlertTitle>
|
||||||
|
<AlertDescription maxWidth="sm">{description}</AlertDescription>
|
||||||
|
<Button mt="2" onClick={expand?.onExpand} colorScheme="red">
|
||||||
|
Show
|
||||||
|
</Button>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Alert status="warning">
|
||||||
|
<AlertIcon boxSize="30px" mr="4" />
|
||||||
|
<AlertTitle fontSize="lg">Sensitive Content</AlertTitle>
|
||||||
|
<AlertDescription maxWidth="sm">{description}</AlertDescription>
|
||||||
|
<Spacer />
|
||||||
|
<Button mt="2" onClick={expand?.onExpand} colorScheme="red">
|
||||||
|
Show
|
||||||
|
</Button>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
@ -26,6 +26,7 @@ export type AppSettings = {
|
|||||||
zapAmounts: number[];
|
zapAmounts: number[];
|
||||||
primaryColor: string;
|
primaryColor: string;
|
||||||
imageProxy: string;
|
imageProxy: string;
|
||||||
|
showContentWarning: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultSettings: AppSettings = {
|
export const defaultSettings: AppSettings = {
|
||||||
@ -39,6 +40,7 @@ export const defaultSettings: AppSettings = {
|
|||||||
zapAmounts: [50, 200, 500, 1000],
|
zapAmounts: [50, 200, 500, 1000],
|
||||||
primaryColor: "#8DB600",
|
primaryColor: "#8DB600",
|
||||||
imageProxy: "",
|
imageProxy: "",
|
||||||
|
showContentWarning: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
function parseAppSettings(event: NostrEvent): AppSettings {
|
function parseAppSettings(event: NostrEvent): AppSettings {
|
||||||
|
@ -44,7 +44,7 @@ function ColorPicker({ value, onPickColor, ...props }: { onPickColor?: (color: s
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function DisplaySettings() {
|
export default function DisplaySettings() {
|
||||||
const { blurImages, colorMode, primaryColor, updateSettings } = useAppSettings();
|
const { blurImages, colorMode, primaryColor, updateSettings, showContentWarning } = useAppSettings();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
@ -106,6 +106,21 @@ export default function DisplaySettings() {
|
|||||||
<span>Enabled: blur images for people you aren't following</span>
|
<span>Enabled: blur images for people you aren't following</span>
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<FormLabel htmlFor="show-content-warning" mb="0">
|
||||||
|
Show content warning
|
||||||
|
</FormLabel>
|
||||||
|
<Switch
|
||||||
|
id="show-content-warning"
|
||||||
|
isChecked={showContentWarning}
|
||||||
|
onChange={(v) => updateSettings({ showContentWarning: v.target.checked })}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<FormHelperText>
|
||||||
|
<span>Enabled: shows a warning for notes with NIP-36 Content Warning</span>
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<FormLabel htmlFor="show-ads" mb="0">
|
<FormLabel htmlFor="show-ads" mb="0">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user