mirror of
https://github.com/umami-software/umami.git
synced 2026-02-04 04:37:11 +01:00
Implements push-based real-time updates for the Sessions page. New sessions now appear instantly without manual reload or polling. Changes: - Add SSE event emitter for session creation notifications - Create SSE stream endpoint at /api/websites/[id]/sessions/stream - Emit session events in tracking endpoint when sessions are created - Add useSessionStream hook to connect to SSE and invalidate queries - Fix LoadingPanel to prevent flicker during background refetches
73 lines
1.7 KiB
TypeScript
73 lines
1.7 KiB
TypeScript
import { Column, type ColumnProps, Loading } from '@umami/react-zen';
|
|
import type { ReactNode } from 'react';
|
|
import { Empty } from '@/components/common/Empty';
|
|
import { ErrorMessage } from '@/components/common/ErrorMessage';
|
|
|
|
export interface LoadingPanelProps extends ColumnProps {
|
|
data?: any;
|
|
error?: unknown;
|
|
isEmpty?: boolean;
|
|
isLoading?: boolean;
|
|
isFetching?: boolean;
|
|
loadingIcon?: 'dots' | 'spinner';
|
|
loadingPlacement?: 'center' | 'absolute' | 'inline';
|
|
renderEmpty?: () => ReactNode;
|
|
children: ReactNode;
|
|
}
|
|
|
|
export function LoadingPanel({
|
|
data,
|
|
error,
|
|
isEmpty,
|
|
isLoading,
|
|
isFetching,
|
|
loadingIcon = 'dots',
|
|
loadingPlacement = 'absolute',
|
|
renderEmpty = () => <Empty />,
|
|
children,
|
|
...props
|
|
}: LoadingPanelProps): ReactNode {
|
|
const empty = isEmpty ?? checkEmpty(data);
|
|
const hasData = data && !empty;
|
|
|
|
// Show loading only on initial load when no data exists yet
|
|
// Don't show loading during background refetches when we already have data
|
|
if ((isLoading || isFetching) && !hasData) {
|
|
return (
|
|
<Column position="relative" height="100%" width="100%" {...props}>
|
|
<Loading icon={loadingIcon} placement={loadingPlacement} />
|
|
</Column>
|
|
);
|
|
}
|
|
|
|
// Show error
|
|
if (error) {
|
|
return <ErrorMessage />;
|
|
}
|
|
|
|
// Show empty state (once loaded)
|
|
if (!error && !isLoading && !isFetching && empty) {
|
|
return renderEmpty();
|
|
}
|
|
|
|
// Show content when we have data (even during background refetch)
|
|
if (hasData) {
|
|
return children;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function checkEmpty(data: any) {
|
|
if (!data) return false;
|
|
|
|
if (Array.isArray(data)) {
|
|
return data.length <= 0;
|
|
}
|
|
|
|
if (typeof data === 'object') {
|
|
return Object.keys(data).length <= 0;
|
|
}
|
|
|
|
return !!data;
|
|
}
|