1
0
forked from Kieran/snort

CONFIG.bypassImgProxyError to load directly from origin

This commit is contained in:
Martti Malmi 2023-11-28 14:20:35 +02:00
parent 07e2c15f54
commit 95aa6f78bb
12 changed files with 93 additions and 88 deletions

View File

@ -12,6 +12,7 @@
"animalNamePlaceholders": false,
"showNoteBroadcaster": true,
"defaultZapPoolFee": 0.5,
"bypassImgProxyError": false,
"features": {
"analytics": true,
"subscriptions": true,

View File

@ -12,6 +12,7 @@
"animalNamePlaceholders": true,
"showNoteBroadcaster": false,
"defaultZapPoolFee": 0.5,
"bypassImgProxyError": true,
"features": {
"analytics": true,
"subscriptions": false,

View File

@ -53,6 +53,7 @@ declare const CONFIG: {
animalNamePlaceholders: boolean;
showNoteBroadcaster: boolean;
defaultZapPoolFee: number;
bypassImgProxyError: boolean;
features: {
analytics: boolean;
subscriptions: boolean;

View File

@ -1,7 +1,8 @@
import { NostrLink, NoteCollection, ReqFilter, RequestBuilder } from "@snort/system";
import { useReactions, useRequestBuilder } from "@snort/system-react";
import { useMemo } from "react";
import { TimelineRenderer } from "./TimelineFragment";
import { TimelineRenderer } from "@/Element/Feed/TimelineRenderer";
export function GenericFeed({ link }: { link: NostrLink }) {
const sub = useMemo(() => {

View File

@ -7,8 +7,8 @@ import { dedupeByPubkey, findTag } from "@/SnortUtils";
import useTimelineFeed, { TimelineFeed, TimelineSubject } from "@/Feed/TimelineFeed";
import useModeration from "@/Hooks/useModeration";
import { LiveStreams } from "@/Element/LiveStreams";
import { TimelineRenderer } from "./TimelineFragment";
import { unixNow } from "@snort/shared";
import { TimelineRenderer } from "@/Element/Feed/TimelineRenderer";
export interface TimelineProps {
postsOnly: boolean;

View File

@ -10,9 +10,9 @@ import useModeration from "@/Hooks/useModeration";
import { FollowsFeed } from "@/Cache";
import { LiveStreams } from "@/Element/LiveStreams";
import useLogin from "@/Hooks/useLogin";
import { TimelineRenderer } from "./TimelineFragment";
import useHashtagsFeed from "@/Feed/HashtagsFeed";
import { ShowMoreInView } from "@/Element/Event/ShowMore";
import { TimelineRenderer } from "@/Element/Feed/TimelineRenderer";
export interface TimelineFollowsProps {
postsOnly: boolean;

View File

@ -1,11 +1,7 @@
import { ReactNode, useCallback } from "react";
import { FormattedMessage } from "react-intl";
import { useInView } from "react-intersection-observer";
import { TaggedNostrEvent } from "@snort/system";
import Note from "@/Element/Event/Note";
import ProfileImage from "@/Element/User/ProfileImage";
import Icon from "@/Icons/Icon";
import { findTag } from "@/SnortUtils";
export interface TimelineFragment {
@ -14,67 +10,6 @@ export interface TimelineFragment {
title?: ReactNode;
}
export interface TimelineRendererProps {
frags: Array<TimelineFragment>;
related: Array<TaggedNostrEvent>;
/**
* List of pubkeys who have posted recently
*/
latest: Array<string>;
showLatest: (toTop: boolean) => void;
noteRenderer?: (ev: TaggedNostrEvent) => ReactNode;
noteOnClick?: (ev: TaggedNostrEvent) => void;
noteContext?: (ev: TaggedNostrEvent) => ReactNode;
}
export function TimelineRenderer(props: TimelineRendererProps) {
const { ref, inView } = useInView();
return (
<>
{props.latest.length > 0 && (
<>
<div className="card latest-notes" onClick={() => props.showLatest(false)} ref={ref}>
{props.latest.slice(0, 3).map(p => {
return <ProfileImage pubkey={p} showUsername={false} link={""} showFollowDistance={false} />;
})}
<FormattedMessage
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
id="3t3kok"
values={{ n: props.latest.length }}
/>
<Icon name="arrowUp" />
</div>
{!inView && (
<div
className="card latest-notes latest-notes-fixed pointer fade-in"
onClick={() => props.showLatest(true)}>
{props.latest.slice(0, 3).map(p => {
return <ProfileImage pubkey={p} showUsername={false} link={""} showFollowDistance={false} />;
})}
<FormattedMessage
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
id="3t3kok"
values={{ n: props.latest.length }}
/>
<Icon name="arrowUp" />
</div>
)}
</>
)}
{props.frags.map(f => (
<TimelineFragment
frag={f}
related={props.related}
noteRenderer={props.noteRenderer}
noteOnClick={props.noteOnClick}
noteContext={props.noteContext}
/>
))}
</>
);
}
export interface TimelineFragProps {
frag: TimelineFragment;
related: Array<TaggedNostrEvent>;

View File

@ -0,0 +1,68 @@
import { useInView } from "react-intersection-observer";
import ProfileImage from "@/Element/User/ProfileImage";
import { FormattedMessage } from "react-intl";
import Icon from "@/Icons/Icon";
import { TaggedNostrEvent } from "@snort/system";
import { ReactNode } from "react";
import { TimelineFragment } from "@/Element/Feed/TimelineFragment";
export interface TimelineRendererProps {
frags: Array<TimelineFragment>;
related: Array<TaggedNostrEvent>;
/**
* List of pubkeys who have posted recently
*/
latest: Array<string>;
showLatest: (toTop: boolean) => void;
noteRenderer?: (ev: TaggedNostrEvent) => ReactNode;
noteOnClick?: (ev: TaggedNostrEvent) => void;
noteContext?: (ev: TaggedNostrEvent) => ReactNode;
}
export function TimelineRenderer(props: TimelineRendererProps) {
const { ref, inView } = useInView();
return (
<>
{props.latest.length > 0 && (
<>
<div className="card latest-notes" onClick={() => props.showLatest(false)} ref={ref}>
{props.latest.slice(0, 3).map(p => {
return <ProfileImage pubkey={p} showUsername={false} link={""} showFollowDistance={false} />;
})}
<FormattedMessage
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
id="3t3kok"
values={{ n: props.latest.length }}
/>
<Icon name="arrowUp" />
</div>
{!inView && (
<div
className="card latest-notes latest-notes-fixed pointer fade-in"
onClick={() => props.showLatest(true)}>
{props.latest.slice(0, 3).map(p => {
return <ProfileImage pubkey={p} showUsername={false} link={""} showFollowDistance={false} />;
})}
<FormattedMessage
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
id="3t3kok"
values={{ n: props.latest.length }}
/>
<Icon name="arrowUp" />
</div>
)}
</>
)}
{props.frags.map(f => (
<TimelineFragment
frag={f}
related={props.related}
noteRenderer={props.noteRenderer}
noteOnClick={props.noteOnClick}
noteContext={props.noteContext}
/>
))}
</>
);
}

View File

@ -5,12 +5,13 @@ import { getUrlHostname } from "@/SnortUtils";
interface ProxyImgProps extends React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement> {
size?: number;
className?: string;
}
export const ProxyImg = (props: ProxyImgProps) => {
const { proxy } = useImgProxy();
const [loadFailed, setLoadFailed] = useState(false);
const [bypass, setBypass] = useState(false);
const [bypass, setBypass] = useState(CONFIG.bypassImgProxyError);
if (loadFailed) {
if (bypass) {

View File

@ -30,6 +30,9 @@
}
.avatar .icons {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
display: flex;

View File

@ -1,11 +1,11 @@
import "./Avatar.css";
import { CSSProperties, ReactNode, useEffect, useState } from "react";
import { ReactNode, useEffect, useState } from "react";
import type { UserMetadata } from "@snort/system";
import classNames from "classnames";
import useImgProxy from "@/Hooks/useImgProxy";
import { defaultAvatar, getDisplayName } from "@/SnortUtils";
import { ProxyImg } from "@/Element/ProxyImg";
interface AvatarProps {
pubkey: string;
@ -20,33 +20,27 @@ interface AvatarProps {
const Avatar = ({ pubkey, user, size, onClick, image, imageOverlay, icons, className }: AvatarProps) => {
const [url, setUrl] = useState("");
const { proxy } = useImgProxy();
useEffect(() => {
setUrl(image ?? user?.picture ?? defaultAvatar(pubkey));
}, [user, image, pubkey]);
const s = size ?? 120;
useEffect(() => {
const url = image ?? user?.picture;
if (url) {
const proxyUrl = proxy(url, s);
setUrl(proxyUrl);
} else {
setUrl(defaultAvatar(pubkey));
}
}, [user, image]);
const backgroundImage = `url(${url})`;
const style = { "--img-url": backgroundImage } as CSSProperties;
const style = {} as React.CSSProperties;
if (size) {
style.width = `${s}px`;
style.height = `${s}px`;
style.width = `${size}px`;
style.height = `${size}px`;
}
const domain = user?.nip05 && user.nip05.split("@")[1];
return (
<div
onClick={onClick}
style={style}
className={classNames("avatar", { "with-overlay": imageOverlay }, className)}
className={classNames("avatar relative", { "with-overlay": imageOverlay }, className)}
data-domain={domain?.toLowerCase()}
title={getDisplayName(user, "")}>
<ProxyImg className="rounded-full" src={url} size={s} alt={getDisplayName(user, "")} />
{icons && <div className="icons">{icons}</div>}
{imageOverlay && <div className="overlay">{imageOverlay}</div>}
</div>

View File

@ -1,5 +1,5 @@
import "./Deck.css";
import { CSSProperties, createContext, useContext, useEffect, useState } from "react";
import { createContext, useContext, useEffect, useState } from "react";
import { Outlet, useNavigate } from "react-router-dom";
import { FormattedMessage } from "react-intl";
import { NostrLink, TaggedNostrEvent } from "@snort/system";