fix: HTTP avatar image rendering issue

This commit is contained in:
codytseng 2026-03-28 15:18:50 +08:00
parent e4a61740c5
commit 5596e5eb7b
9 changed files with 40 additions and 33 deletions

View file

@ -2,7 +2,7 @@ import { Button } from '@/components/ui/button'
import { Slider } from '@/components/ui/slider'
import { isInsecureUrl } from '@/lib/url'
import { cn } from '@/lib/utils'
import storage from '@/services/local-storage.service'
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import mediaManager from '@/services/media-manager.service'
import { Minimize2, Pause, Play, X } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
@ -24,6 +24,7 @@ export default function AudioPlayer({
className
}: AudioPlayerProps) {
const audioRef = useRef<HTMLAudioElement>(null)
const { allowInsecureConnection } = useUserPreferences()
const [isPlaying, setIsPlaying] = useState(false)
const [currentTime, setCurrentTime] = useState(0)
const [duration, setDuration] = useState(0)
@ -123,7 +124,7 @@ export default function AudioPlayer({
}, 300)
}
if (error || (!storage.getAllowInsecureConnection() && isInsecureUrl(src))) {
if (error || (!allowInsecureConnection && isInsecureUrl(src))) {
return <ExternalLink url={src} />
}

View file

@ -1,6 +1,6 @@
import { isInsecureUrl } from '@/lib/url'
import { cn } from '@/lib/utils'
import storage from '@/services/local-storage.service'
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import { TEmoji } from '@/types'
import { Heart } from 'lucide-react'
import { HTMLAttributes, useState } from 'react'
@ -15,6 +15,7 @@ export default function Emoji({
img?: string
}
}) {
const { allowInsecureConnection } = useUserPreferences()
const [hasError, setHasError] = useState(false)
if (typeof emoji === 'string') {
@ -25,7 +26,7 @@ export default function Emoji({
)
}
if (hasError || (!storage.getAllowInsecureConnection() && isInsecureUrl(emoji.url))) {
if (hasError || (!allowInsecureConnection && isInsecureUrl(emoji.url))) {
return (
<span className={cn('whitespace-nowrap', classNames?.text)}>{`:${emoji.shortcode}:`}</span>
)

View file

@ -1,14 +1,13 @@
import { Skeleton } from '@/components/ui/skeleton'
import { isInsecureUrl } from '@/lib/url'
import { cn } from '@/lib/utils'
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import blossomService from '@/services/blossom.service'
import storage from '@/services/local-storage.service'
import { TImetaInfo } from '@/types'
import { decode } from 'blurhash'
import { ImageOff } from 'lucide-react'
import { HTMLAttributes, useEffect, useMemo, useRef, useState } from 'react'
import { thumbHashToDataURL } from 'thumbhash'
import ExternalLink from '../ExternalLink'
export default function Image({
image: { url, blurHash, thumbHash, pubkey, dim },
@ -29,21 +28,20 @@ export default function Image({
hideIfError?: boolean
errorPlaceholder?: React.ReactNode
}) {
const { allowInsecureConnection } = useUserPreferences()
const [isLoading, setIsLoading] = useState(true)
const [displaySkeleton, setDisplaySkeleton] = useState(true)
const [hasError, setHasError] = useState(false)
const [isBlocked, setIsBlocked] = useState(false)
const [imageUrl, setImageUrl] = useState<string>()
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
useEffect(() => {
setIsLoading(true)
setHasError(false)
setIsBlocked(false)
setDisplaySkeleton(true)
if (!storage.getAllowInsecureConnection() && isInsecureUrl(url)) {
setIsBlocked(true)
if (!allowInsecureConnection && isInsecureUrl(url)) {
setHasError(true)
setIsLoading(false)
return
}
@ -62,11 +60,7 @@ export default function Image({
} else {
setImageUrl(url)
}
}, [url])
if (isBlocked) {
return <ExternalLink url={url} />
}
}, [url, allowInsecureConnection])
if (hideIfError && hasError) return null

View file

@ -1,11 +1,11 @@
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
import { Skeleton } from '@/components/ui/skeleton'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import { useFetchProfile } from '@/hooks'
import { toProfile } from '@/lib/link'
import { generateImageByPubkey } from '@/lib/pubkey'
import { cn, isTouchDevice } from '@/lib/utils'
import { SecondaryPageLink } from '@/PageManager'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import { useMemo } from 'react'
import Image from '../Image'
import ProfileCard from '../ProfileCard'

View file

@ -2,14 +2,13 @@ import { isInsecureUrl } from '@/lib/url'
import { cn, isInViewport } from '@/lib/utils'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import storage from '@/services/local-storage.service'
import mediaManager from '@/services/media-manager.service'
import { useEffect, useRef, useState } from 'react'
import ExternalLink from '../ExternalLink'
export default function VideoPlayer({ src, className }: { src: string; className?: string }) {
const { autoplay, videoLoop } = useContentPolicy()
const { muteMedia, updateMuteMedia } = useUserPreferences()
const { muteMedia, updateMuteMedia, allowInsecureConnection } = useUserPreferences()
const [error, setError] = useState(false)
const videoRef = useRef<HTMLVideoElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
@ -71,7 +70,7 @@ export default function VideoPlayer({ src, className }: { src: string; className
}
}, [muteMedia])
if (error || (!storage.getAllowInsecureConnection() && isInsecureUrl(src))) {
if (error || (!allowInsecureConnection && isInsecureUrl(src))) {
return <ExternalLink url={src} />
}

View file

@ -3,10 +3,10 @@ import { isInsecureUrl } from '@/lib/url'
import { cn } from '@/lib/utils'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import storage from '@/services/local-storage.service'
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import { useMemo } from 'react'
import Image from '../Image'
import ExternalLink from '../ExternalLink'
import Image from '../Image'
export default function WebPreview({
url,
@ -18,6 +18,7 @@ export default function WebPreview({
mustLoad?: boolean
}) {
const { autoLoadMedia } = useContentPolicy()
const { allowInsecureConnection } = useUserPreferences()
const { isSmallScreen } = useScreenSize()
const { title, description, image } = useFetchWebMetadata(url)
@ -29,7 +30,7 @@ export default function WebPreview({
}
}, [url])
if (!storage.getAllowInsecureConnection() && isInsecureUrl(url)) {
if (!allowInsecureConnection && isInsecureUrl(url)) {
return null
}

View file

@ -1,10 +1,11 @@
import { isInsecureUrl } from '@/lib/url'
import storage from '@/services/local-storage.service'
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import webService from '@/services/web.service'
import { TWebMetadata } from '@/types'
import { useEffect, useState } from 'react'
export function useFetchWebMetadata(url: string) {
const { allowInsecureConnection } = useUserPreferences()
const [metadata, setMetadata] = useState<TWebMetadata>({})
const proxyServer = import.meta.env.VITE_PROXY_SERVER
if (proxyServer) {
@ -12,10 +13,10 @@ export function useFetchWebMetadata(url: string) {
}
useEffect(() => {
if (!storage.getAllowInsecureConnection() && isInsecureUrl(url)) return
if (!allowInsecureConnection && isInsecureUrl(url)) return
webService.fetchWebMetadata(url).then((metadata) => setMetadata(metadata))
}, [url])
}, [url, allowInsecureConnection])
return metadata
}

View file

@ -6,6 +6,7 @@ import { Switch } from '@/components/ui/switch'
import { DEFAULT_FAVICON_URL_TEMPLATE } from '@/constants'
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import storage from '@/services/local-storage.service'
import { forwardRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -13,12 +14,10 @@ import { useTranslation } from 'react-i18next'
const SystemSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
const { t } = useTranslation()
const { faviconUrlTemplate, setFaviconUrlTemplate } = useContentPolicy()
const { allowInsecureConnection, updateAllowInsecureConnection } = useUserPreferences()
const [filterOutOnionRelays, setFilterOutOnionRelays] = useState(
storage.getFilterOutOnionRelays()
)
const [allowInsecureConnection, setAllowInsecureConnection] = useState(
storage.getAllowInsecureConnection()
)
return (
<SecondaryPageLayout ref={ref} index={index} title={t('System')}>
@ -58,10 +57,7 @@ const SystemSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
<Switch
id="allow-insecure-connection"
checked={allowInsecureConnection}
onCheckedChange={(checked) => {
storage.setAllowInsecureConnection(checked)
setAllowInsecureConnection(checked)
}}
onCheckedChange={updateAllowInsecureConnection}
/>
</div>
<div className="space-y-2 px-4">

View file

@ -21,6 +21,9 @@ type TUserPreferencesContext = {
quickReactionEmoji: string | TEmoji
updateQuickReactionEmoji: (emoji: string | TEmoji) => void
allowInsecureConnection: boolean
updateAllowInsecureConnection: (allow: boolean) => void
}
const UserPreferencesContext = createContext<TUserPreferencesContext | undefined>(undefined)
@ -46,6 +49,10 @@ export function UserPreferencesProvider({ children }: { children: React.ReactNod
const [quickReaction, setQuickReaction] = useState(storage.getQuickReaction())
const [quickReactionEmoji, setQuickReactionEmoji] = useState(storage.getQuickReactionEmoji())
const [allowInsecureConnection, setAllowInsecureConnection] = useState(
storage.getAllowInsecureConnection()
)
useEffect(() => {
if (!isSmallScreen && enableSingleColumnLayout) {
document.documentElement.style.setProperty('overflow-y', 'scroll')
@ -79,6 +86,11 @@ export function UserPreferencesProvider({ children }: { children: React.ReactNod
storage.setQuickReactionEmoji(emoji)
}
const updateAllowInsecureConnection = (allow: boolean) => {
setAllowInsecureConnection(allow)
storage.setAllowInsecureConnection(allow)
}
return (
<UserPreferencesContext.Provider
value={{
@ -93,7 +105,9 @@ export function UserPreferencesProvider({ children }: { children: React.ReactNod
quickReaction,
updateQuickReaction,
quickReactionEmoji,
updateQuickReactionEmoji
updateQuickReactionEmoji,
allowInsecureConnection,
updateAllowInsecureConnection
}}
>
{children}