diff --git a/apps/frontend/src/components/Layout/Header.tsx b/apps/frontend/src/components/Layout/Header.tsx index 4bac00c..b5dd2e7 100644 --- a/apps/frontend/src/components/Layout/Header.tsx +++ b/apps/frontend/src/components/Layout/Header.tsx @@ -96,21 +96,26 @@ export function Header() { diff --git a/apps/frontend/src/pages/ProfileSettingsPage.tsx b/apps/frontend/src/pages/ProfileSettingsPage.tsx index 9b51f78..fcc6e43 100644 --- a/apps/frontend/src/pages/ProfileSettingsPage.tsx +++ b/apps/frontend/src/pages/ProfileSettingsPage.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; @@ -25,6 +25,7 @@ export function ProfileSettingsPage() { const refresh = useAuthStore((s) => s.refresh); const queryClient = useQueryClient(); const fileInputRef = useRef(null); + const [avatarFailed, setAvatarFailed] = useState(false); const { data, isLoading } = useQuery({ queryKey: ['profile'], @@ -120,6 +121,10 @@ export function ProfileSettingsPage() { const avatarPreview = watch('avatarUrl') || data?.avatarUrl; + useEffect(() => { + setAvatarFailed(false); + }, [avatarPreview]); + return (
@@ -136,8 +141,13 @@ export function ProfileSettingsPage() {
- {avatarPreview ? ( - + {avatarPreview && !avatarFailed ? ( + setAvatarFailed(true)} + /> ) : ( )} diff --git a/apps/frontend/src/pages/PublicProfilePage.tsx b/apps/frontend/src/pages/PublicProfilePage.tsx index 25d4d1a..99de48b 100644 --- a/apps/frontend/src/pages/PublicProfilePage.tsx +++ b/apps/frontend/src/pages/PublicProfilePage.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { Link, useParams } from 'react-router-dom'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import type { @@ -19,6 +19,7 @@ export function PublicProfilePage() { const user = useAuthStore((s) => s.user); const { slug = '' } = useParams<{ slug: string }>(); const queryClient = useQueryClient(); + const [avatarFailed, setAvatarFailed] = useState(false); const profile = useQuery({ queryKey: ['public-profile', slug], @@ -57,6 +58,10 @@ export function PublicProfilePage() { return () => window.clearTimeout(t); }, [wishes.data, markSeen, queryClient, slug]); + useEffect(() => { + setAvatarFailed(false); + }, [profile.data?.avatarUrl]); + return (
@@ -87,11 +92,12 @@ export function PublicProfilePage() { <>
- {profile.data.avatarUrl ? ( + {profile.data.avatarUrl && !avatarFailed ? ( setAvatarFailed(true)} /> ) : ( diff --git a/apps/frontend/src/styles/global.css b/apps/frontend/src/styles/global.css index 387c403..741e3f1 100644 --- a/apps/frontend/src/styles/global.css +++ b/apps/frontend/src/styles/global.css @@ -64,7 +64,13 @@ @apply bg-primary text-primary-foreground shadow-card hover:bg-primary; } .app-header__actions { - @apply flex shrink-0 items-center gap-1 justify-self-start lg:justify-self-end; + @apply flex shrink-0 items-center gap-1 justify-self-start whitespace-nowrap lg:justify-self-end; + } + .app-header__action { + @apply shrink-0; + } + .app-header__action-text { + @apply hidden xl:inline; } .app-footer { diff --git a/docker/nginx.conf b/docker/nginx.conf index 9fe37e4..57bae01 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -4,13 +4,6 @@ server { root /usr/share/nginx/html; index index.html; - # Static files with long cache - location ~* \.(?:js|css|woff2?|svg|png|jpg|jpeg|gif|webp|ico)$ { - expires 7d; - add_header Cache-Control "public"; - try_files $uri =404; - } - # API proxy location /api/ { proxy_pass http://family-wishlist-backend:3000/api/; @@ -24,13 +17,20 @@ server { } # Uploaded files (images) - location /uploads/ { + location ^~ /uploads/ { proxy_pass http://family-wishlist-backend:3000/uploads/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_cache_valid 200 1h; } + # Static files with long cache + location ~* \.(?:js|css|woff2?|svg|png|jpg|jpeg|gif|webp|ico)$ { + expires 7d; + add_header Cache-Control "public"; + try_files $uri =404; + } + # SPA fallback location / { try_files $uri /index.html;