mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-10-04 16:37:00 +02:00
Merge branch 'next'
This commit is contained in:
5
.changeset/eight-dryers-drive.md
Normal file
5
.changeset/eight-dryers-drive.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Make photo flush with edge of note
|
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
|
5
.changeset/rude-beds-attack.md
Normal file
5
.changeset/rude-beds-attack.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Replace laggy photo lightbox
|
5
.changeset/tidy-trains-tap.md
Normal file
5
.changeset/tidy-trains-tap.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix subscription id too long
|
@@ -19,7 +19,6 @@
|
|||||||
"idb": "^7.1.1",
|
"idb": "^7.1.1",
|
||||||
"identicon.js": "^2.3.3",
|
"identicon.js": "^2.3.3",
|
||||||
"light-bolt11-decoder": "^2.1.0",
|
"light-bolt11-decoder": "^2.1.0",
|
||||||
"lightgallery": "^2.7.1",
|
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"noble-secp256k1": "^1.2.14",
|
"noble-secp256k1": "^1.2.14",
|
||||||
"nostr-tools": "^1.8.3",
|
"nostr-tools": "^1.8.3",
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
import { Box, Image, ImageProps, Link, useDisclosure } from "@chakra-ui/react";
|
import { Box, Image, ImageProps, Link, useDisclosure } from "@chakra-ui/react";
|
||||||
import { EmbedableContent, embedJSX } from "../../helpers/embeds";
|
import { EmbedableContent, embedJSX } from "../../helpers/embeds";
|
||||||
import appSettings from "../../services/app-settings";
|
import appSettings from "../../services/app-settings";
|
||||||
|
import { ImageGalleryLink } from "../image-gallery";
|
||||||
import LightGallery from "lightgallery/react";
|
|
||||||
import lgThumbnail from "lightgallery/plugins/thumbnail";
|
|
||||||
import lgZoom from "lightgallery/plugins/zoom";
|
|
||||||
|
|
||||||
const BlurredImage = (props: ImageProps) => {
|
const BlurredImage = (props: ImageProps) => {
|
||||||
const { isOpen, onOpen } = useDisclosure();
|
const { isOpen, onOpen } = useDisclosure();
|
||||||
@@ -28,11 +25,9 @@ export function embedImages(content: EmbedableContent, trusted = false) {
|
|||||||
const src = match[0];
|
const src = match[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LightGallery plugins={[lgThumbnail, lgZoom]} licenseKey="1234-5678-9101-1121">
|
<ImageGalleryLink href={src} target="_blank" display="block" mx="-2">
|
||||||
<Link href={src} target="_blank" display="inline-block">
|
<ImageComponent src={thumbnail} cursor="pointer" maxW="30rem" w="full" />
|
||||||
<ImageComponent src={thumbnail} cursor="pointer" maxW="30rem" />
|
</ImageGalleryLink>
|
||||||
</Link>
|
|
||||||
</LightGallery>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
name: "Image",
|
name: "Image",
|
||||||
|
76
src/components/image-gallery.tsx
Normal file
76
src/components/image-gallery.tsx
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { DownloadIcon } from "@chakra-ui/icons";
|
||||||
|
import {
|
||||||
|
LinkProps,
|
||||||
|
Link,
|
||||||
|
useDisclosure,
|
||||||
|
Modal,
|
||||||
|
ModalOverlay,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalCloseButton,
|
||||||
|
ModalBody,
|
||||||
|
Image,
|
||||||
|
ModalFooter,
|
||||||
|
Button,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { PropsWithChildren, createContext, useContext, useState } from "react";
|
||||||
|
|
||||||
|
const GalleryContext = createContext({
|
||||||
|
isOpen: false,
|
||||||
|
openImage(url: string) {},
|
||||||
|
});
|
||||||
|
export function useGalleryContext() {
|
||||||
|
return useContext(GalleryContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ImageGalleryLink({ children, href, ...props }: Omit<LinkProps, "onClick">) {
|
||||||
|
const { openImage } = useGalleryContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
{...props}
|
||||||
|
href={href}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (href) {
|
||||||
|
e.preventDefault();
|
||||||
|
openImage(href);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ImageGalleryProvider({ children }: PropsWithChildren) {
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
const [image, setImage] = useState("");
|
||||||
|
|
||||||
|
const openImage = (url: string) => {
|
||||||
|
setImage(url);
|
||||||
|
onOpen();
|
||||||
|
};
|
||||||
|
const context = { isOpen, openImage };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GalleryContext.Provider value={context}>
|
||||||
|
{children}
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose} size="6xl">
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader>Image</ModalHeader>
|
||||||
|
<ModalCloseButton />
|
||||||
|
<ModalBody p="0">
|
||||||
|
<Image src={image} w="full" />
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button colorScheme="brand" onClick={onClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</GalleryContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
@@ -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]);
|
||||||
|
|
||||||
@@ -74,12 +100,8 @@ export const Note = React.memo(({ event, maxHeight, variant = "outline" }: NoteP
|
|||||||
</Link>
|
</Link>
|
||||||
</Flex>
|
</Flex>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody px="2" py="0">
|
<CardBody p="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">
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { Box } from "@chakra-ui/react";
|
import { Box, Text } from "@chakra-ui/react";
|
||||||
import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event";
|
import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event";
|
||||||
import styled from "@emotion/styled";
|
import styled from "@emotion/styled";
|
||||||
import { useExpand } from "./expanded";
|
import { useExpand } from "./expanded";
|
||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
embedAppleMusic,
|
embedAppleMusic,
|
||||||
embedNostrHashtags,
|
embedNostrHashtags,
|
||||||
} from "../embed-types";
|
} from "../embed-types";
|
||||||
|
import { ImageGalleryProvider } from "../image-gallery";
|
||||||
|
|
||||||
function buildContents(event: NostrEvent | DraftNostrEvent, trusted: boolean = false) {
|
function buildContents(event: NostrEvent | DraftNostrEvent, trusted: boolean = false) {
|
||||||
let content: EmbedableContent = [event.content];
|
let content: EmbedableContent = [event.content];
|
||||||
@@ -82,19 +83,28 @@ export const NoteContents = React.memo(({ event, trusted, maxHeight }: NoteConte
|
|||||||
const showOverlay = !!maxHeight && !expand?.expanded && innerHeight > maxHeight;
|
const showOverlay = !!maxHeight && !expand?.expanded && innerHeight > maxHeight;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<ImageGalleryProvider>
|
||||||
whiteSpace="pre-wrap"
|
<Box
|
||||||
maxHeight={!expand?.expanded ? maxHeight : undefined}
|
whiteSpace="pre-wrap"
|
||||||
position="relative"
|
maxHeight={!expand?.expanded ? maxHeight : undefined}
|
||||||
overflow={maxHeight && !expand?.expanded ? "hidden" : "initial"}
|
position="relative"
|
||||||
onLoad={() => testHeight()}
|
overflow={maxHeight && !expand?.expanded ? "hidden" : "initial"}
|
||||||
>
|
onLoad={() => testHeight()}
|
||||||
<div ref={ref}>
|
px="2"
|
||||||
{content.map((part, i) => (
|
>
|
||||||
<span key={"part-" + i}>{part}</span>
|
<div ref={ref}>
|
||||||
))}
|
{content.map((part, i) =>
|
||||||
</div>
|
typeof part === "string" ? (
|
||||||
{showOverlay && <GradientOverlay onClick={expand?.onExpand} />}
|
<Text as="span" key={"part-" + i}>
|
||||||
</Box>
|
{part}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
React.cloneElement(part, { key: "part-" + i })
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{showOverlay && <GradientOverlay onClick={expand?.onExpand} />}
|
||||||
|
</Box>
|
||||||
|
</ImageGalleryProvider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
@@ -3,10 +3,6 @@ import { createRoot } from "react-dom/client";
|
|||||||
import { App } from "./app";
|
import { App } from "./app";
|
||||||
import { Providers } from "./providers";
|
import { Providers } from "./providers";
|
||||||
|
|
||||||
import "lightgallery/css/lightgallery.css";
|
|
||||||
import "lightgallery/css/lg-zoom.css";
|
|
||||||
import "lightgallery/css/lg-thumbnail.css";
|
|
||||||
|
|
||||||
// register nostr: protocol handler
|
// register nostr: protocol handler
|
||||||
try {
|
try {
|
||||||
navigator.registerProtocolHandler("web+nostr", new URL("/l/%s", location.origin).toString());
|
navigator.registerProtocolHandler("web+nostr", new URL("/l/%s", location.origin).toString());
|
||||||
|
@@ -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 {
|
||||||
|
@@ -3,7 +3,7 @@ import { Button, Flex, Spinner } from "@chakra-ui/react";
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { Note } from "../../components/note";
|
import { Note } from "../../components/note";
|
||||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||||
import { isReply } from "../../helpers/nostr-event";
|
import { isReply, truncatedId } from "../../helpers/nostr-event";
|
||||||
import { useAppTitle } from "../../hooks/use-app-title";
|
import { useAppTitle } from "../../hooks/use-app-title";
|
||||||
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||||
import { useCurrentAccount } from "../../hooks/use-current-account";
|
import { useCurrentAccount } from "../../hooks/use-current-account";
|
||||||
@@ -69,7 +69,7 @@ export default function DiscoverTab() {
|
|||||||
const throttledPubkeys = useThrottle(pubkeys, 1000);
|
const throttledPubkeys = useThrottle(pubkeys, 1000);
|
||||||
|
|
||||||
const { events, loading, loadMore } = useTimelineLoader(
|
const { events, loading, loadMore } = useTimelineLoader(
|
||||||
`${account.pubkey}-discover`,
|
`${truncatedId(account.pubkey)}-discover`,
|
||||||
relays,
|
relays,
|
||||||
{ authors: throttledPubkeys, kinds: [1], since: moment().subtract(1, "hour").unix() },
|
{ authors: throttledPubkeys, kinds: [1], since: moment().subtract(1, "hour").unix() },
|
||||||
{ pageSize: moment.duration(1, "hour").asSeconds(), enabled: throttledPubkeys.length > 0 }
|
{ pageSize: moment.duration(1, "hour").asSeconds(), enabled: throttledPubkeys.length > 0 }
|
||||||
|
@@ -2,7 +2,7 @@ import { Button, Flex, FormControl, FormLabel, Spinner, Switch } from "@chakra-u
|
|||||||
import { useSearchParams } from "react-router-dom";
|
import { useSearchParams } from "react-router-dom";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { Note } from "../../components/note";
|
import { Note } from "../../components/note";
|
||||||
import { isReply } from "../../helpers/nostr-event";
|
import { isReply, truncatedId } from "../../helpers/nostr-event";
|
||||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||||
import { useUserContacts } from "../../hooks/use-user-contacts";
|
import { useUserContacts } from "../../hooks/use-user-contacts";
|
||||||
import { AddIcon } from "@chakra-ui/icons";
|
import { AddIcon } from "@chakra-ui/icons";
|
||||||
@@ -25,7 +25,7 @@ export default function FollowingTab() {
|
|||||||
|
|
||||||
const following = contacts?.contacts || [];
|
const following = contacts?.contacts || [];
|
||||||
const { events, loading, loadMore } = useTimelineLoader(
|
const { events, loading, loadMore } = useTimelineLoader(
|
||||||
`${account.pubkey}-following-posts`,
|
`${truncatedId(account.pubkey)}-following-posts`,
|
||||||
readRelays,
|
readRelays,
|
||||||
{ authors: following, kinds: [1, 6], since: moment().subtract(2, "hour").unix() },
|
{ authors: following, kinds: [1, 6], since: moment().subtract(2, "hour").unix() },
|
||||||
{ pageSize: moment.duration(2, "hour").asSeconds(), enabled: following.length > 0 }
|
{ pageSize: moment.duration(2, "hour").asSeconds(), enabled: following.length > 0 }
|
||||||
|
@@ -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">
|
||||||
|
Reference in New Issue
Block a user