CONFIG.bypassImgProxyError to load directly from origin
This commit is contained in:
parent
07e2c15f54
commit
95aa6f78bb
@ -12,6 +12,7 @@
|
||||
"animalNamePlaceholders": false,
|
||||
"showNoteBroadcaster": true,
|
||||
"defaultZapPoolFee": 0.5,
|
||||
"bypassImgProxyError": false,
|
||||
"features": {
|
||||
"analytics": true,
|
||||
"subscriptions": true,
|
||||
|
@ -12,6 +12,7 @@
|
||||
"animalNamePlaceholders": true,
|
||||
"showNoteBroadcaster": false,
|
||||
"defaultZapPoolFee": 0.5,
|
||||
"bypassImgProxyError": true,
|
||||
"features": {
|
||||
"analytics": true,
|
||||
"subscriptions": false,
|
||||
|
1
packages/app/custom.d.ts
vendored
1
packages/app/custom.d.ts
vendored
@ -53,6 +53,7 @@ declare const CONFIG: {
|
||||
animalNamePlaceholders: boolean;
|
||||
showNoteBroadcaster: boolean;
|
||||
defaultZapPoolFee: number;
|
||||
bypassImgProxyError: boolean;
|
||||
features: {
|
||||
analytics: boolean;
|
||||
subscriptions: boolean;
|
||||
|
@ -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(() => {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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>;
|
||||
|
68
packages/app/src/Element/Feed/TimelineRenderer.tsx
Normal file
68
packages/app/src/Element/Feed/TimelineRenderer.tsx
Normal 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}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
@ -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) {
|
||||
|
@ -30,6 +30,9 @@
|
||||
}
|
||||
|
||||
.avatar .icons {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
@ -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>
|
||||
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user