CONFIG.bypassImgProxyError to load directly from origin
This commit is contained in:
parent
07e2c15f54
commit
95aa6f78bb
@ -12,6 +12,7 @@
|
|||||||
"animalNamePlaceholders": false,
|
"animalNamePlaceholders": false,
|
||||||
"showNoteBroadcaster": true,
|
"showNoteBroadcaster": true,
|
||||||
"defaultZapPoolFee": 0.5,
|
"defaultZapPoolFee": 0.5,
|
||||||
|
"bypassImgProxyError": false,
|
||||||
"features": {
|
"features": {
|
||||||
"analytics": true,
|
"analytics": true,
|
||||||
"subscriptions": true,
|
"subscriptions": true,
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"animalNamePlaceholders": true,
|
"animalNamePlaceholders": true,
|
||||||
"showNoteBroadcaster": false,
|
"showNoteBroadcaster": false,
|
||||||
"defaultZapPoolFee": 0.5,
|
"defaultZapPoolFee": 0.5,
|
||||||
|
"bypassImgProxyError": true,
|
||||||
"features": {
|
"features": {
|
||||||
"analytics": true,
|
"analytics": true,
|
||||||
"subscriptions": false,
|
"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;
|
animalNamePlaceholders: boolean;
|
||||||
showNoteBroadcaster: boolean;
|
showNoteBroadcaster: boolean;
|
||||||
defaultZapPoolFee: number;
|
defaultZapPoolFee: number;
|
||||||
|
bypassImgProxyError: boolean;
|
||||||
features: {
|
features: {
|
||||||
analytics: boolean;
|
analytics: boolean;
|
||||||
subscriptions: boolean;
|
subscriptions: boolean;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { NostrLink, NoteCollection, ReqFilter, RequestBuilder } from "@snort/system";
|
import { NostrLink, NoteCollection, ReqFilter, RequestBuilder } from "@snort/system";
|
||||||
import { useReactions, useRequestBuilder } from "@snort/system-react";
|
import { useReactions, useRequestBuilder } from "@snort/system-react";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { TimelineRenderer } from "./TimelineFragment";
|
|
||||||
|
import { TimelineRenderer } from "@/Element/Feed/TimelineRenderer";
|
||||||
|
|
||||||
export function GenericFeed({ link }: { link: NostrLink }) {
|
export function GenericFeed({ link }: { link: NostrLink }) {
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
|
@ -7,8 +7,8 @@ import { dedupeByPubkey, findTag } from "@/SnortUtils";
|
|||||||
import useTimelineFeed, { TimelineFeed, TimelineSubject } from "@/Feed/TimelineFeed";
|
import useTimelineFeed, { TimelineFeed, TimelineSubject } from "@/Feed/TimelineFeed";
|
||||||
import useModeration from "@/Hooks/useModeration";
|
import useModeration from "@/Hooks/useModeration";
|
||||||
import { LiveStreams } from "@/Element/LiveStreams";
|
import { LiveStreams } from "@/Element/LiveStreams";
|
||||||
import { TimelineRenderer } from "./TimelineFragment";
|
|
||||||
import { unixNow } from "@snort/shared";
|
import { unixNow } from "@snort/shared";
|
||||||
|
import { TimelineRenderer } from "@/Element/Feed/TimelineRenderer";
|
||||||
|
|
||||||
export interface TimelineProps {
|
export interface TimelineProps {
|
||||||
postsOnly: boolean;
|
postsOnly: boolean;
|
||||||
|
@ -10,9 +10,9 @@ import useModeration from "@/Hooks/useModeration";
|
|||||||
import { FollowsFeed } from "@/Cache";
|
import { FollowsFeed } from "@/Cache";
|
||||||
import { LiveStreams } from "@/Element/LiveStreams";
|
import { LiveStreams } from "@/Element/LiveStreams";
|
||||||
import useLogin from "@/Hooks/useLogin";
|
import useLogin from "@/Hooks/useLogin";
|
||||||
import { TimelineRenderer } from "./TimelineFragment";
|
|
||||||
import useHashtagsFeed from "@/Feed/HashtagsFeed";
|
import useHashtagsFeed from "@/Feed/HashtagsFeed";
|
||||||
import { ShowMoreInView } from "@/Element/Event/ShowMore";
|
import { ShowMoreInView } from "@/Element/Event/ShowMore";
|
||||||
|
import { TimelineRenderer } from "@/Element/Feed/TimelineRenderer";
|
||||||
|
|
||||||
export interface TimelineFollowsProps {
|
export interface TimelineFollowsProps {
|
||||||
postsOnly: boolean;
|
postsOnly: boolean;
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import { ReactNode, useCallback } from "react";
|
import { ReactNode, useCallback } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
|
||||||
import { useInView } from "react-intersection-observer";
|
|
||||||
import { TaggedNostrEvent } from "@snort/system";
|
import { TaggedNostrEvent } from "@snort/system";
|
||||||
|
|
||||||
import Note from "@/Element/Event/Note";
|
import Note from "@/Element/Event/Note";
|
||||||
import ProfileImage from "@/Element/User/ProfileImage";
|
|
||||||
import Icon from "@/Icons/Icon";
|
|
||||||
import { findTag } from "@/SnortUtils";
|
import { findTag } from "@/SnortUtils";
|
||||||
|
|
||||||
export interface TimelineFragment {
|
export interface TimelineFragment {
|
||||||
@ -14,67 +10,6 @@ export interface TimelineFragment {
|
|||||||
title?: ReactNode;
|
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 {
|
export interface TimelineFragProps {
|
||||||
frag: TimelineFragment;
|
frag: TimelineFragment;
|
||||||
related: Array<TaggedNostrEvent>;
|
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> {
|
interface ProxyImgProps extends React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement> {
|
||||||
size?: number;
|
size?: number;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProxyImg = (props: ProxyImgProps) => {
|
export const ProxyImg = (props: ProxyImgProps) => {
|
||||||
const { proxy } = useImgProxy();
|
const { proxy } = useImgProxy();
|
||||||
const [loadFailed, setLoadFailed] = useState(false);
|
const [loadFailed, setLoadFailed] = useState(false);
|
||||||
const [bypass, setBypass] = useState(false);
|
const [bypass, setBypass] = useState(CONFIG.bypassImgProxyError);
|
||||||
|
|
||||||
if (loadFailed) {
|
if (loadFailed) {
|
||||||
if (bypass) {
|
if (bypass) {
|
||||||
|
@ -30,6 +30,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.avatar .icons {
|
.avatar .icons {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import "./Avatar.css";
|
import "./Avatar.css";
|
||||||
|
|
||||||
import { CSSProperties, ReactNode, useEffect, useState } from "react";
|
import { ReactNode, useEffect, useState } from "react";
|
||||||
import type { UserMetadata } from "@snort/system";
|
import type { UserMetadata } from "@snort/system";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
import useImgProxy from "@/Hooks/useImgProxy";
|
|
||||||
import { defaultAvatar, getDisplayName } from "@/SnortUtils";
|
import { defaultAvatar, getDisplayName } from "@/SnortUtils";
|
||||||
|
import { ProxyImg } from "@/Element/ProxyImg";
|
||||||
|
|
||||||
interface AvatarProps {
|
interface AvatarProps {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
@ -20,33 +20,27 @@ interface AvatarProps {
|
|||||||
|
|
||||||
const Avatar = ({ pubkey, user, size, onClick, image, imageOverlay, icons, className }: AvatarProps) => {
|
const Avatar = ({ pubkey, user, size, onClick, image, imageOverlay, icons, className }: AvatarProps) => {
|
||||||
const [url, setUrl] = useState("");
|
const [url, setUrl] = useState("");
|
||||||
const { proxy } = useImgProxy();
|
|
||||||
|
useEffect(() => {
|
||||||
|
setUrl(image ?? user?.picture ?? defaultAvatar(pubkey));
|
||||||
|
}, [user, image, pubkey]);
|
||||||
|
|
||||||
const s = size ?? 120;
|
const s = size ?? 120;
|
||||||
useEffect(() => {
|
const style = {} as React.CSSProperties;
|
||||||
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;
|
|
||||||
if (size) {
|
if (size) {
|
||||||
style.width = `${s}px`;
|
style.width = `${size}px`;
|
||||||
style.height = `${s}px`;
|
style.height = `${size}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const domain = user?.nip05 && user.nip05.split("@")[1];
|
const domain = user?.nip05 && user.nip05.split("@")[1];
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
style={style}
|
style={style}
|
||||||
className={classNames("avatar", { "with-overlay": imageOverlay }, className)}
|
className={classNames("avatar relative", { "with-overlay": imageOverlay }, className)}
|
||||||
data-domain={domain?.toLowerCase()}
|
data-domain={domain?.toLowerCase()}
|
||||||
title={getDisplayName(user, "")}>
|
title={getDisplayName(user, "")}>
|
||||||
|
<ProxyImg className="rounded-full" src={url} size={s} alt={getDisplayName(user, "")} />
|
||||||
{icons && <div className="icons">{icons}</div>}
|
{icons && <div className="icons">{icons}</div>}
|
||||||
{imageOverlay && <div className="overlay">{imageOverlay}</div>}
|
{imageOverlay && <div className="overlay">{imageOverlay}</div>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import "./Deck.css";
|
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 { Outlet, useNavigate } from "react-router-dom";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||||
|
Loading…
Reference in New Issue
Block a user