LCOV - code coverage report
Current view: top level - dashql-app/src/session/persistence - session_url_share_overlay.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 98.7 % 77 76
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import * as styles from './session_url_share_overlay.module.css';
       4              : 
       5              : import { AnchorAlignment } from '../../ui/foundations/anchored_position.js';
       6              : import { AnchoredOverlay } from '../../ui/foundations/anchored_overlay.js';
       7              : import { SessionExportSettings, SessionExportSettingsView } from './session_export_settings_view.js';
       8              : import { classNames } from '../../utils/classnames.js';
       9              : import { exportSessionAsUrl, SessionLinkTarget } from './session_export.js';
      10              : import { connectionParamsHaveLoginHint, getConnectionParamsFromStateDetails } from '../../connections/connection_params.js';
      11              : import { resolveSessionConnections, useConnectionReferences, useConnectionRegistry, useSessionConnections } from '../../connections/connection_registry.js';
      12              : import { useRouteContext } from '../../app/router/router.js';
      13              : import { useSessionScripts } from '../scripts/session_scripts_registry.js';
      14              : import { useStorageReader } from '../../storage/storage_provider.js';
      15              : import { ShareURLField } from './share_url_field.js';
      16              : 
      17              : interface Props {
      18              :     className?: string;
      19              :     isOpen: boolean;
      20              :     sessionId?: string;
      21              :     setIsOpen: (v: boolean) => void;
      22              :     anchorRef?: React.RefObject<HTMLButtonElement | null>;
      23              : }
      24              : 
      25              : 
      26            1 : export const SessionURLShareOverlay: React.FC<Props> = (props: Props) => {
      27            1 :     const route = useRouteContext();
      28            1 :     const anchorRef = React.createRef<HTMLDivElement>();
      29            1 :     const buttonRef = React.createRef<HTMLButtonElement>();
      30              : 
      31            1 :     const [sessionScripts] = useSessionScripts(props.sessionId ?? route.sessionId ?? null);
      32            1 :     const connections = useSessionConnections(sessionScripts?.sessionId ?? null);
      33              :     // The connections are resolved anew on every render, the link follows their ids
      34            1 :     const connectionIds = connections?.map(connection => connection.connectionId).join(',') ?? null;
      35            1 :     const [databaseRegistry] = useConnectionRegistry();
      36            1 :     const references = useConnectionReferences();
      37            1 :     const storage = useStorageReader();
      38            1 :     const [publicURLText, setPublicURLText] = React.useState<string | null>(null);
      39            1 :     const [settings, setSettings] = React.useState<SessionExportSettings>({
      40            1 :         withCatalog: false,
      41            1 :         withLoginHint: true,
      42            1 :         preserveUUIDs: false,
      43            1 :     });
      44              : 
      45            1 :     React.useEffect(() => {
      46            1 :         if (!props.isOpen) {
      47            1 :             return;
      48              :         }
      49              : 
      50            1 :         let cancelled = false;
      51            1 :         setPublicURLText(null);
      52              : 
      53            1 :         async function generateURL() {
      54            1 :             let setupUrl: URL | null = null;
      55            1 :             if (sessionScripts != null && connectionIds != null) {
      56            1 :                 const attached = resolveSessionConnections(databaseRegistry, references, sessionScripts.sessionId);
      57            1 :                 if (attached != null) {
      58            1 :                     const params = new Map();
      59            1 :                     const connectionNames = new Map<string, string>();
      60            1 :                     for (const database of attached) {
      61            1 :                         const connectionParams = database.params;
      62            1 :                         if (connectionParams != null) params.set(database.connectionId, connectionParams);
      63            1 :                         if (database.name != null) connectionNames.set(database.connectionId, database.name);
      64            1 :                     }
      65            1 :                     setupUrl = await exportSessionAsUrl(
      66            1 :                         storage.backend,
      67            1 :                         sessionScripts.sessionId,
      68            1 :                         params,
      69            1 :                         SessionLinkTarget.WEB,
      70            1 :                         { ...settings, connectionNames },
      71            1 :                     );
      72            1 :                 }
      73            1 :             }
      74              : 
      75            1 :             if (!cancelled) setPublicURLText(setupUrl?.toString() ?? null);
      76            1 :         }
      77              : 
      78            1 :         generateURL().catch(() => {
      79            0 :             if (!cancelled) setPublicURLText(null);
      80            1 :         });
      81              : 
      82            1 :         return () => {
      83            1 :             cancelled = true;
      84            1 :         };
      85            1 :     }, [databaseRegistry, references, settings, sessionScripts, connectionIds, props.isOpen]);
      86              : 
      87            1 :     const hasLoginHint = React.useMemo(
      88            1 :         () => (connections ?? []).some(connection => connectionParamsHaveLoginHint(getConnectionParamsFromStateDetails(connection.details))),
      89            1 :         [connections],
      90            1 :     );
      91              : 
      92            1 :     return (
      93            1 :         <AnchoredOverlay
      94            1 :             renderAnchor={props.anchorRef ? null : () => <div ref={anchorRef} />}
      95            1 :             open={props.isOpen}
      96            1 :             onClose={() => props.setIsOpen(false)}
      97            1 :             anchorRef={props.anchorRef ?? anchorRef}
      98            1 :             returnFocusRef={props.anchorRef}
      99            1 :             align={AnchorAlignment.End}
     100            1 :             overlayProps={{
     101            1 :                 initialFocusRef: buttonRef,
     102            1 :             }}
     103              :         >
     104            1 :             <div className={classNames(styles.sharing_overlay, props.className)}>
     105            1 :                 <ShareURLField url={publicURLText} ariaLabel="Session share URL" buttonRef={buttonRef} />
     106            1 :                 <SessionExportSettingsView
     107            1 :                     withCatalog={true}
     108            1 :                     withLoginHint={hasLoginHint}
     109            1 :                     settings={settings}
     110            1 :                     setSettings={setSettings}
     111            1 :                 />
     112            1 :             </div>
     113            1 :         </AnchoredOverlay>
     114              :     );
     115            1 : };
        

Generated by: LCOV version 2.0-1