mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-19 20:11:31 +02:00
add option to hide media
This commit is contained in:
@@ -62,6 +62,8 @@
|
|||||||
- make app handle image files
|
- make app handle image files
|
||||||
- block notes based on content
|
- block notes based on content
|
||||||
- implement NIP-56 and blocking
|
- implement NIP-56 and blocking
|
||||||
|
- allow user to select relay or following list when fetching replies (default to my relays + following?)
|
||||||
|
- filter list of followers by users the user has blocked/reported (stops bots/spammers from showing up at followers)
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
@@ -109,3 +109,9 @@ export const RelayIcon = createIcon({
|
|||||||
d: "M11 14v-3h2v3h5a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h5zM2.51 8.837C3.835 4.864 7.584 2 12 2s8.166 2.864 9.49 6.837l-1.898.632a8.003 8.003 0 0 0-15.184 0l-1.897-.632zm3.796 1.265a6.003 6.003 0 0 1 11.388 0l-1.898.633a4.002 4.002 0 0 0-7.592 0l-1.898-.633z",
|
d: "M11 14v-3h2v3h5a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h5zM2.51 8.837C3.835 4.864 7.584 2 12 2s8.166 2.864 9.49 6.837l-1.898.632a8.003 8.003 0 0 0-15.184 0l-1.897-.632zm3.796 1.265a6.003 6.003 0 0 1 11.388 0l-1.898.633a4.002 4.002 0 0 0-7.592 0l-1.898-.633z",
|
||||||
defaultProps,
|
defaultProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const ExternalLinkIcon = createIcon({
|
||||||
|
displayName: "eternal-link-icon",
|
||||||
|
d: "M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm11-3v8h-2V6.413l-7.793 7.794-1.414-1.414L17.585 5H13V3h8z",
|
||||||
|
defaultProps,
|
||||||
|
});
|
||||||
|
@@ -1,5 +1,15 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { AspectRatio, Box, Image, ImageProps, Link, PinInputField, useDisclosure } from "@chakra-ui/react";
|
import {
|
||||||
|
AspectRatio,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
ButtonGroup,
|
||||||
|
IconButton,
|
||||||
|
Image,
|
||||||
|
ImageProps,
|
||||||
|
Link,
|
||||||
|
useDisclosure,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
import { InlineInvoiceCard } from "../inline-invoice-card";
|
import { InlineInvoiceCard } from "../inline-invoice-card";
|
||||||
import { TweetEmbed } from "../tweet-embed";
|
import { TweetEmbed } from "../tweet-embed";
|
||||||
import { UserLink } from "../user-link";
|
import { UserLink } from "../user-link";
|
||||||
@@ -7,6 +17,7 @@ import { normalizeToHex } from "../../helpers/nip-19";
|
|||||||
import { NostrEvent } from "../../types/nostr-event";
|
import { NostrEvent } from "../../types/nostr-event";
|
||||||
import { NoteLink } from "../note-link";
|
import { NoteLink } from "../note-link";
|
||||||
import settings from "../../services/settings";
|
import settings from "../../services/settings";
|
||||||
|
// import { ExternalLinkIcon } from "../icons";
|
||||||
|
|
||||||
const BlurredImage = (props: ImageProps) => {
|
const BlurredImage = (props: ImageProps) => {
|
||||||
const { isOpen, onToggle } = useDisclosure();
|
const { isOpen, onToggle } = useDisclosure();
|
||||||
@@ -16,16 +27,19 @@ const BlurredImage = (props: ImageProps) => {
|
|||||||
const embeds: {
|
const embeds: {
|
||||||
regexp: RegExp;
|
regexp: RegExp;
|
||||||
render: (match: RegExpMatchArray, event?: NostrEvent, trusted?: boolean) => JSX.Element | string;
|
render: (match: RegExpMatchArray, event?: NostrEvent, trusted?: boolean) => JSX.Element | string;
|
||||||
|
isMedia: boolean;
|
||||||
}[] = [
|
}[] = [
|
||||||
// Lightning Invoice
|
// Lightning Invoice
|
||||||
{
|
{
|
||||||
regexp: /(lightning:)?(LNBC[A-Za-z0-9]+)/im,
|
regexp: /(lightning:)?(LNBC[A-Za-z0-9]+)/im,
|
||||||
render: (match) => <InlineInvoiceCard paymentRequest={match[2]} />,
|
render: (match) => <InlineInvoiceCard paymentRequest={match[2]} />,
|
||||||
|
isMedia: false,
|
||||||
},
|
},
|
||||||
// Twitter tweet
|
// Twitter tweet
|
||||||
{
|
{
|
||||||
regexp: /^https?:\/\/twitter\.com\/(?:\#!\/)?(\w+)\/status(es)?\/(\d+)[^\s]+/im,
|
regexp: /^https?:\/\/twitter\.com\/(?:\#!\/)?(\w+)\/status(es)?\/(\d+)[^\s]+/im,
|
||||||
render: (match) => <TweetEmbed href={match[0]} conversation={false} />,
|
render: (match) => <TweetEmbed href={match[0]} conversation={false} />,
|
||||||
|
isMedia: true,
|
||||||
},
|
},
|
||||||
// Youtube Video
|
// Youtube Video
|
||||||
{
|
{
|
||||||
@@ -43,6 +57,7 @@ const embeds: {
|
|||||||
></iframe>
|
></iframe>
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
),
|
),
|
||||||
|
isMedia: true,
|
||||||
},
|
},
|
||||||
// Youtube Music
|
// Youtube Music
|
||||||
{
|
{
|
||||||
@@ -58,6 +73,7 @@ const embeds: {
|
|||||||
></iframe>
|
></iframe>
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
),
|
),
|
||||||
|
isMedia: true,
|
||||||
},
|
},
|
||||||
// Tidal
|
// Tidal
|
||||||
{
|
{
|
||||||
@@ -69,6 +85,7 @@ const embeds: {
|
|||||||
height="96"
|
height="96"
|
||||||
></iframe>
|
></iframe>
|
||||||
),
|
),
|
||||||
|
isMedia: true,
|
||||||
},
|
},
|
||||||
// Spotify
|
// Spotify
|
||||||
{
|
{
|
||||||
@@ -86,6 +103,7 @@ const embeds: {
|
|||||||
src={`https://open.spotify.com/embed/${match[1]}/${match[2]}`}
|
src={`https://open.spotify.com/embed/${match[1]}/${match[2]}`}
|
||||||
></iframe>
|
></iframe>
|
||||||
),
|
),
|
||||||
|
isMedia: true,
|
||||||
},
|
},
|
||||||
// apple music
|
// apple music
|
||||||
{
|
{
|
||||||
@@ -100,6 +118,7 @@ const embeds: {
|
|||||||
src={match[0].replace("music.apple.com", "embed.music.apple.com")}
|
src={match[0].replace("music.apple.com", "embed.music.apple.com")}
|
||||||
></iframe>
|
></iframe>
|
||||||
),
|
),
|
||||||
|
isMedia: true,
|
||||||
},
|
},
|
||||||
// Image
|
// Image
|
||||||
{
|
{
|
||||||
@@ -108,6 +127,7 @@ const embeds: {
|
|||||||
const ImageComponent = trusted || !settings.blurImages.value ? Image : BlurredImage;
|
const ImageComponent = trusted || !settings.blurImages.value ? Image : BlurredImage;
|
||||||
return <ImageComponent src={match[0]} width="100%" maxWidth="30rem" />;
|
return <ImageComponent src={match[0]} width="100%" maxWidth="30rem" />;
|
||||||
},
|
},
|
||||||
|
isMedia: true,
|
||||||
},
|
},
|
||||||
// Video
|
// Video
|
||||||
{
|
{
|
||||||
@@ -117,6 +137,7 @@ const embeds: {
|
|||||||
<video src={match[0]} controls />
|
<video src={match[0]} controls />
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
),
|
),
|
||||||
|
isMedia: true,
|
||||||
},
|
},
|
||||||
// Link
|
// Link
|
||||||
{
|
{
|
||||||
@@ -126,6 +147,7 @@ const embeds: {
|
|||||||
{match[0]}
|
{match[0]}
|
||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
|
isMedia: false,
|
||||||
},
|
},
|
||||||
// npub1 and note1 links
|
// npub1 and note1 links
|
||||||
{
|
{
|
||||||
@@ -139,6 +161,7 @@ const embeds: {
|
|||||||
return match[0];
|
return match[0];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isMedia: false,
|
||||||
},
|
},
|
||||||
// Nostr Mention Links
|
// Nostr Mention Links
|
||||||
{
|
{
|
||||||
@@ -158,26 +181,41 @@ const embeds: {
|
|||||||
|
|
||||||
return match[0];
|
return match[0];
|
||||||
},
|
},
|
||||||
|
isMedia: false,
|
||||||
},
|
},
|
||||||
// bold text
|
// bold text
|
||||||
{
|
{
|
||||||
regexp: /\*\*([^\n]+)\*\*/im,
|
regexp: /\*\*([^\n]+)\*\*/im,
|
||||||
render: (match) => <span style={{ fontWeight: "bold" }}>{match[1]}</span>,
|
render: (match) => <span style={{ fontWeight: "bold" }}>{match[1]}</span>,
|
||||||
|
isMedia: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const MediaEmbed = ({ children }: { children: JSX.Element | string }) => {
|
||||||
|
const [show, setShow] = useState(settings.autoShowMedia.value);
|
||||||
|
|
||||||
|
return show ? (
|
||||||
|
<>{children}</>
|
||||||
|
) : (
|
||||||
|
<ButtonGroup size="sm" isAttached variant="outline">
|
||||||
|
<Button onClick={() => setShow(true)}>Show Embed</Button>
|
||||||
|
{/* TODO: add external link for embed */}
|
||||||
|
{/* <IconButton as="a" aria-label="Add to friends" icon={<ExternalLinkIcon />} href={}/> */}
|
||||||
|
</ButtonGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
function embedContent(content: string, event?: NostrEvent, trusted: boolean = false): (string | JSX.Element)[] {
|
function embedContent(content: string, event?: NostrEvent, trusted: boolean = false): (string | JSX.Element)[] {
|
||||||
for (const { regexp, render } of embeds) {
|
for (const embedType of embeds) {
|
||||||
const match = content.match(regexp);
|
const match = content.match(embedType.regexp);
|
||||||
|
|
||||||
if (match && match.index !== undefined) {
|
if (match && match.index !== undefined) {
|
||||||
const before = content.slice(0, match.index);
|
const before = content.slice(0, match.index);
|
||||||
const after = content.slice(match.index + match[0].length, content.length);
|
const after = content.slice(match.index + match[0].length, content.length);
|
||||||
return [
|
const embedRender = embedType.render(match, event, trusted);
|
||||||
...embedContent(before, event, trusted),
|
const embed = embedType.isMedia ? <MediaEmbed>{embedRender}</MediaEmbed> : embedRender;
|
||||||
render(match, event, trusted),
|
|
||||||
...embedContent(after, event, trusted),
|
return [...embedContent(before, event, trusted), embed, ...embedContent(after, event, trusted)];
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [content];
|
return [content];
|
||||||
|
@@ -4,7 +4,7 @@ import { truncatedId } from "./nostr-event";
|
|||||||
|
|
||||||
export function getUserDisplayName(metadata: Kind0ParsedContent | undefined, pubkey: string) {
|
export function getUserDisplayName(metadata: Kind0ParsedContent | undefined, pubkey: string) {
|
||||||
if (metadata?.display_name && metadata?.name) {
|
if (metadata?.display_name && metadata?.name) {
|
||||||
return `${metadata.display_name} (${metadata.name})`;
|
return metadata.display_name;
|
||||||
} else if (metadata?.name) {
|
} else if (metadata?.name) {
|
||||||
return metadata.name;
|
return metadata.name;
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ const settings = {
|
|||||||
relays: new BehaviorSubject<string[]>([]),
|
relays: new BehaviorSubject<string[]>([]),
|
||||||
identity: new BehaviorSubject<SavedIdentity | null>(null),
|
identity: new BehaviorSubject<SavedIdentity | null>(null),
|
||||||
blurImages: new BehaviorSubject(true),
|
blurImages: new BehaviorSubject(true),
|
||||||
|
autoShowMedia: new BehaviorSubject(true),
|
||||||
};
|
};
|
||||||
|
|
||||||
async function loadSettings() {
|
async function loadSettings() {
|
||||||
|
@@ -34,6 +34,7 @@ export const SettingsView = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const relays = useSubject(settings.relays);
|
const relays = useSubject(settings.relays);
|
||||||
const blurImages = useSubject(settings.blurImages);
|
const blurImages = useSubject(settings.blurImages);
|
||||||
|
const autoShowMedia = useSubject(settings.autoShowMedia);
|
||||||
const [relayInputValue, setRelayInputValue] = useState("");
|
const [relayInputValue, setRelayInputValue] = useState("");
|
||||||
|
|
||||||
const { colorMode, setColorMode } = useColorMode();
|
const { colorMode, setColorMode } = useColorMode();
|
||||||
@@ -155,6 +156,16 @@ export const SettingsView = () => {
|
|||||||
onChange={(v) => settings.blurImages.next(v.target.checked)}
|
onChange={(v) => settings.blurImages.next(v.target.checked)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl display="flex" alignItems="center">
|
||||||
|
<FormLabel htmlFor="auto-show-embeds" mb="0">
|
||||||
|
Automatically show media
|
||||||
|
</FormLabel>
|
||||||
|
<Switch
|
||||||
|
id="auto-show-embeds"
|
||||||
|
isChecked={autoShowMedia}
|
||||||
|
onChange={(v) => settings.autoShowMedia.next(v.target.checked)}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
<FormControl display="flex" alignItems="center">
|
<FormControl display="flex" alignItems="center">
|
||||||
<FormLabel htmlFor="show-ads" mb="0">
|
<FormLabel htmlFor="show-ads" mb="0">
|
||||||
Show Ads
|
Show Ads
|
||||||
|
Reference in New Issue
Block a user