diff --git a/client/src/App.tsx b/client/src/App.tsx index 4179c1a9..31ac7787 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -93,7 +93,15 @@ export default function App() { useEffect(() => { if (!location.pathname.startsWith('/shared/') && !location.pathname.startsWith('/public/') && !location.pathname.startsWith('/login')) { - loadUser() + // If the persist snapshot already has an authenticated user, validate + // silently so the PWA shell renders immediately without a spinner. + const alreadyAuthenticated = useAuthStore.getState().isAuthenticated + if (alreadyAuthenticated) { + useAuthStore.setState({ isLoading: false }) + loadUser({ silent: true }) + } else { + loadUser() + } } authApi.getAppConfig().then(async (config: { demo_mode?: boolean; dev_mode?: boolean; is_prerelease?: boolean; has_maps_key?: boolean; version?: string; timezone?: string; require_mfa?: boolean; trip_reminders_enabled?: boolean; permissions?: Record }) => { if (config?.demo_mode) setDemoMode(true) diff --git a/client/src/components/Collab/CollabNotes.tsx b/client/src/components/Collab/CollabNotes.tsx index 2585cabf..2d6f253c 100644 --- a/client/src/components/Collab/CollabNotes.tsx +++ b/client/src/components/Collab/CollabNotes.tsx @@ -7,6 +7,7 @@ import remarkBreaks from 'remark-breaks' import { Plus, Trash2, Pin, PinOff, Pencil, X, Check, StickyNote, Settings, ExternalLink, Maximize2, Loader2 } from 'lucide-react' import { collabApi } from '../../api/client' import { getAuthUrl } from '../../api/authUrl' +import { openFile } from '../../utils/fileDownload' import { useCanDo } from '../../store/permissionsStore' import { useTripStore } from '../../store/tripStore' import { addListener, removeListener } from '../../api/websocket' @@ -111,10 +112,7 @@ function FilePreviewPortal({ file, onClose }: FilePreviewPortalProps) { const isPdf = file.mime_type === 'application/pdf' const isTxt = file.mime_type?.startsWith('text/') - const openInNewTab = async () => { - const u = await getAuthUrl(rawUrl, 'download') - window.open(u, '_blank', 'noreferrer') - } + const openInNewTab = () => openFile(rawUrl).catch(() => {}) return ReactDOM.createPortal(
diff --git a/client/src/components/Files/FileManager.tsx b/client/src/components/Files/FileManager.tsx index b4b4e200..6092806a 100644 --- a/client/src/components/Files/FileManager.tsx +++ b/client/src/components/Files/FileManager.tsx @@ -10,6 +10,7 @@ import { useCanDo } from '../../store/permissionsStore' import { useTripStore } from '../../store/tripStore' import { getAuthUrl } from '../../api/authUrl' +import { downloadFile, openFile } from '../../utils/fileDownload' function isImage(mimeType) { if (!mimeType) return false @@ -30,16 +31,8 @@ function formatSize(bytes) { return `${(bytes / 1024 / 1024).toFixed(1)} MB` } -async function triggerDownload(url: string, filename: string) { - const authUrl = await getAuthUrl(url, 'download') - const res = await fetch(authUrl) - const blob = await res.blob() - const a = document.createElement('a') - a.href = URL.createObjectURL(blob) - a.download = filename - document.body.appendChild(a) - a.click() - setTimeout(() => { URL.revokeObjectURL(a.href); a.remove() }, 100) +function triggerDownload(url: string, filename: string) { + downloadFile(url, filename).catch(() => {}) } function formatDateWithLocale(dateStr, locale) { @@ -120,7 +113,7 @@ function ImageLightbox({ files, initialIndex, onClose }: ImageLightboxProps) {
+

diff --git a/client/src/components/Planner/PlaceInspector.tsx b/client/src/components/Planner/PlaceInspector.tsx index 58ffbf99..be1da19b 100644 --- a/client/src/components/Planner/PlaceInspector.tsx +++ b/client/src/components/Planner/PlaceInspector.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react' -import { getAuthUrl } from '../../api/authUrl' +import { openFile } from '../../utils/fileDownload' import Markdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { X, Clock, MapPin, ExternalLink, Phone, Euro, Edit2, Trash2, Plus, Minus, ChevronDown, ChevronUp, FileText, Upload, File, FileImage, Star, Navigation, Users, Mountain, TrendingUp } from 'lucide-react' @@ -589,7 +589,7 @@ export default function PlaceInspector({ {filesExpanded && placeFiles.length > 0 && (
{placeFiles.map(f => ( -