Prompt for upload re-auth on demand
This commit is contained in:
+105
-17
@@ -3,8 +3,10 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import AutoTextarea from '@/components/AutoTextarea';
|
||||
import { StorageSessionPrompt } from '@/components/StorageSessionPrompt';
|
||||
import { useToast } from '@/lib/contexts/ToastContext';
|
||||
import { useAccentColor } from '@/lib/contexts/AccentColorContext';
|
||||
import { refreshStorageSession } from '@/lib/storage/client';
|
||||
|
||||
export default function AdminPage() {
|
||||
const { showToast } = useToast();
|
||||
@@ -27,6 +29,11 @@ export default function AdminPage() {
|
||||
const [savingSettings, setSavingSettings] = useState(false);
|
||||
const [isUploadingBanner, setIsUploadingBanner] = useState(false);
|
||||
const [bannerUploadError, setBannerUploadError] = useState<string | null>(null);
|
||||
const [showBannerSessionPrompt, setShowBannerSessionPrompt] = useState(false);
|
||||
const [pendingBannerFile, setPendingBannerFile] = useState<File | null>(null);
|
||||
const [bannerPassword, setBannerPassword] = useState('');
|
||||
const [bannerPromptError, setBannerPromptError] = useState('');
|
||||
const [isRefreshingBannerSession, setIsRefreshingBannerSession] = useState(false);
|
||||
const [isUploadingLogo, setIsUploadingLogo] = useState(false);
|
||||
const [logoUploadError, setLogoUploadError] = useState<string | null>(null);
|
||||
const [isUploadingFavicon, setIsUploadingFavicon] = useState(false);
|
||||
@@ -92,11 +99,7 @@ export default function AdminPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleBannerUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
event.target.value = '';
|
||||
if (!file) return;
|
||||
|
||||
const uploadBannerFile = async (file: File, allowPrompt = true) => {
|
||||
setBannerUploadError(null);
|
||||
setIsUploadingBanner(true);
|
||||
|
||||
@@ -110,6 +113,13 @@ export default function AdminPage() {
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok || !data.url) {
|
||||
if (res.status === 401 && allowPrompt) {
|
||||
setPendingBannerFile(file);
|
||||
setBannerPromptError('');
|
||||
setShowBannerSessionPrompt(true);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(data.error || 'Upload failed');
|
||||
}
|
||||
|
||||
@@ -119,6 +129,10 @@ export default function AdminPage() {
|
||||
};
|
||||
setNodeSettings(nextSettings);
|
||||
await handleSaveSettings(nextSettings);
|
||||
setPendingBannerFile(null);
|
||||
setShowBannerSessionPrompt(false);
|
||||
setBannerPassword('');
|
||||
setBannerPromptError('');
|
||||
} catch (error) {
|
||||
console.error('Banner upload failed', error);
|
||||
setBannerUploadError(error instanceof Error ? error.message : 'Upload failed. Please try again.');
|
||||
@@ -127,6 +141,47 @@ export default function AdminPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleBannerUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
event.target.value = '';
|
||||
if (!file) return;
|
||||
|
||||
await uploadBannerFile(file);
|
||||
};
|
||||
|
||||
const handleBannerSessionSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!pendingBannerFile) {
|
||||
setShowBannerSessionPrompt(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bannerPassword.trim()) {
|
||||
setBannerPromptError('Please enter your password');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsRefreshingBannerSession(true);
|
||||
setBannerPromptError('');
|
||||
|
||||
try {
|
||||
await refreshStorageSession(bannerPassword.trim());
|
||||
await uploadBannerFile(pendingBannerFile, false);
|
||||
} catch (error) {
|
||||
setBannerPromptError(error instanceof Error ? error.message : 'Failed to confirm password');
|
||||
} finally {
|
||||
setIsRefreshingBannerSession(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBannerSessionCancel = () => {
|
||||
setShowBannerSessionPrompt(false);
|
||||
setPendingBannerFile(null);
|
||||
setBannerPassword('');
|
||||
setBannerPromptError('');
|
||||
};
|
||||
|
||||
const handleLogoUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
event.target.value = '';
|
||||
@@ -382,15 +437,34 @@ export default function AdminPage() {
|
||||
)}
|
||||
</div>
|
||||
{nodeSettings.bannerUrl && (
|
||||
<div style={{ marginTop: '8px', height: '120px', borderRadius: '8px', overflow: 'hidden', border: '1px solid var(--border)', position: 'relative' }}>
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0,
|
||||
background: `url(${nodeSettings.bannerUrl}) center/cover no-repeat`
|
||||
}} />
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0,
|
||||
background: 'linear-gradient(to bottom, transparent, var(--background-secondary))'
|
||||
}} />
|
||||
<div style={{ marginTop: '12px' }}>
|
||||
<div style={{ fontSize: '12px', color: 'var(--foreground-tertiary)', marginBottom: '8px' }}>
|
||||
Sidebar preview
|
||||
</div>
|
||||
<div
|
||||
className="card"
|
||||
style={{
|
||||
padding: 0,
|
||||
overflow: 'hidden',
|
||||
maxWidth: '360px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: '140px',
|
||||
background: `url(${nodeSettings.bannerUrl}) center/cover no-repeat`,
|
||||
borderBottom: '1px solid var(--border)',
|
||||
}}
|
||||
/>
|
||||
<div style={{ padding: '16px' }}>
|
||||
<h3 style={{ fontWeight: 600, marginBottom: '12px' }}>
|
||||
Welcome to {nodeSettings.name || 'Your Node'}
|
||||
</h3>
|
||||
<p style={{ color: 'var(--foreground-secondary)', fontSize: '14px', lineHeight: 1.6 }}>
|
||||
{nodeSettings.description || 'A brief tagline for your node.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -530,9 +604,23 @@ export default function AdminPage() {
|
||||
<button className="btn btn-primary" onClick={() => handleSaveSettings()} disabled={savingSettings}>
|
||||
{savingSettings ? 'Saving...' : 'Save Settings'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<StorageSessionPrompt
|
||||
open={showBannerSessionPrompt}
|
||||
isSubmitting={isRefreshingBannerSession}
|
||||
password={bannerPassword}
|
||||
error={bannerPromptError}
|
||||
description="Please confirm your password to continue uploading this banner to your storage."
|
||||
onPasswordChange={(nextPassword) => {
|
||||
setBannerPassword(nextPassword);
|
||||
setBannerPromptError('');
|
||||
}}
|
||||
onSubmit={handleBannerSessionSubmit}
|
||||
onCancel={handleBannerSessionCancel}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user