feat: add WebDAV file browser with auth improvements
Add a new Files section powered by WebDAV for browsing, uploading, downloading, renaming, and deleting files and folders. New features: - WebDAV file browser with grid/list views and breadcrumb navigation - File upload (drag-and-drop and button), folder creation, rename, delete - File preview modals for images and other file types - WebDAV proxy API route to handle authentication - Navigation rail entry for Files (auto-hidden when WebDAV is unsupported) Auth improvements: - Fix premature redirects on calendar, contacts, and settings pages by adding explicit auth check on mount before redirecting to login - Persist active settings tab in localStorage Other: - Expose getAuthHeader() and getServerUrl() on JMAPClient - Add WebDAV store with connection testing and capability detection - Add i18n translations for file browser in all 8 locales (de, en, es, fr, it, ja, nl, pt)
This commit is contained in:
@@ -31,19 +31,33 @@ export default function SettingsPage() {
|
||||
const router = useRouter();
|
||||
const t = useTranslations('settings');
|
||||
const tSidebar = useTranslations('sidebar');
|
||||
const { client, isAuthenticated, logout } = useAuthStore();
|
||||
const { client, isAuthenticated, logout, checkAuth, isLoading: authLoading } = useAuthStore();
|
||||
const [initialCheckDone, setInitialCheckDone] = useState(() => useAuthStore.getState().isAuthenticated && !!useAuthStore.getState().client);
|
||||
const { quota, isPushConnected } = useEmailStore();
|
||||
const { stalwartFeaturesEnabled } = useConfig();
|
||||
const [activeTab, setActiveTab] = useState<Tab>('appearance');
|
||||
const [activeTab, setActiveTab] = useState<Tab>(() => {
|
||||
try {
|
||||
const saved = localStorage.getItem('settings-active-tab');
|
||||
if (saved) return saved as Tab;
|
||||
} catch { /* ignore */ }
|
||||
return 'appearance';
|
||||
});
|
||||
const [mobileShowContent, setMobileShowContent] = useState(false);
|
||||
const isDesktop = useIsDesktop();
|
||||
|
||||
// Check auth on mount
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
checkAuth().finally(() => {
|
||||
setInitialCheckDone(true);
|
||||
});
|
||||
}, [checkAuth]);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialCheckDone && !isAuthenticated && !authLoading) {
|
||||
try { sessionStorage.setItem('redirect_after_login', window.location.pathname); } catch { /* ignore */ }
|
||||
router.push('/login');
|
||||
}
|
||||
}, [isAuthenticated, router]);
|
||||
}, [initialCheckDone, isAuthenticated, authLoading, router]);
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return null;
|
||||
@@ -70,6 +84,7 @@ export default function SettingsPage() {
|
||||
|
||||
const handleTabSelect = (tabId: Tab) => {
|
||||
setActiveTab(tabId);
|
||||
try { localStorage.setItem('settings-active-tab', tabId); } catch { /* ignore */ }
|
||||
if (!isDesktop) {
|
||||
setMobileShowContent(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user