Line data Source code
1 : import * as React from 'react';
2 : import * as styles from './session_storage_overlay.module.css';
3 :
4 : import { XIcon, FileDirectoryIcon } from '../../ui/foundations/symbol_icon.js';
5 :
6 : import { AnchorAlignment, AnchorSide } from '../../ui/foundations/anchored_position.js';
7 : import { AnchoredOverlay } from '../../ui/foundations/anchored_overlay.js';
8 : import { ButtonVariant, IconButton } from '../../ui/foundations/button.js';
9 : import { OverlaySize } from '../../ui/foundations/overlay.js';
10 : import { PlatformType, usePlatformType } from '../../platform/platform_type.js';
11 : import { StorageBackendType } from '../../storage/storage_backend.js';
12 : import { displayPath } from '../../storage/session_locator.js';
13 : import { useStorageReader, useStorageWriter } from '../../storage/storage_provider.js';
14 : import { useLogger } from '../../platform/logger/logger_provider.js';
15 : import { relocateSessionToNative } from './session_relocation.js';
16 :
17 : /// The shared header for the storage view: title on the left, actions + close button on the right.
18 1 : function StorageViewHeader(props: {
19 : onClose: () => void;
20 : canRelocate?: boolean;
21 : migrating?: boolean;
22 : onRelocate?: () => void;
23 1 : }) {
24 1 : return (
25 1 : <div className={styles.header_container}>
26 1 : <div className={styles.header_left_container}>
27 1 : <div className={styles.title}>Session Storage</div>
28 1 : </div>
29 1 : <div className={styles.header_right_container}>
30 1 : {props.canRelocate && (
31 0 : <IconButton
32 0 : variant={ButtonVariant.Invisible}
33 0 : aria-label="Store locally"
34 0 : disabled={props.migrating}
35 0 : onClick={props.onRelocate}
36 : >
37 0 : <FileDirectoryIcon />
38 0 : </IconButton>
39 : )}
40 1 : <IconButton
41 1 : variant={ButtonVariant.Invisible}
42 1 : aria-label="close-overlay"
43 1 : onClick={props.onClose}
44 : >
45 1 : <XIcon />
46 1 : </IconButton>
47 1 : </div>
48 1 : </div>
49 : );
50 1 : }
51 :
52 : /// A single parameter row (label + monospace value).
53 1 : function ParamRow(props: { label: string; value: string }) {
54 1 : return (
55 1 : <div className={styles.param_row}>
56 1 : <div className={styles.param_label}>{props.label}</div>
57 1 : <div className={styles.param_value} title={props.value}>{props.value}</div>
58 1 : </div>
59 : );
60 1 : }
61 :
62 : interface SessionStorageViewerProps {
63 : sessionId: string | null;
64 : onClose: () => void;
65 : }
66 :
67 1 : export const SessionStorageViewer: React.FC<SessionStorageViewerProps> = (props) => {
68 1 : const reader = useStorageReader();
69 1 : const writer = useStorageWriter();
70 1 : const logger = useLogger();
71 1 : const platform = usePlatformType();
72 :
73 1 : const [migrating, setMigrating] = React.useState(false);
74 :
75 1 : const location = props.sessionId ? reader.getSessionLocation(props.sessionId) : null;
76 1 : const isNative = location?.type === StorageBackendType.Native;
77 1 : const canRelocate =
78 1 : platform === PlatformType.DESKTOP &&
79 0 : !isNative &&
80 0 : props.sessionId != null &&
81 0 : reader.sessionFolders != null;
82 :
83 1 : const onRelocate = React.useCallback(async () => {
84 0 : const sessionFolders = reader.sessionFolders;
85 0 : if (props.sessionId == null || sessionFolders == null) return;
86 0 : setMigrating(true);
87 0 : try {
88 0 : await relocateSessionToNative(props.sessionId, sessionFolders, writer, logger);
89 0 : } catch {
90 0 : setMigrating(false);
91 0 : }
92 1 : }, [props.sessionId, reader.sessionFolders, writer, logger]);
93 :
94 1 : const backendValue = isNative ? 'Host File System' : 'Origin Private File System (Browser)';
95 1 : const schemaValue = props.sessionId && location ? displayPath(props.sessionId, location) : '';
96 :
97 1 : return (
98 1 : <div className={styles.body}>
99 1 : <StorageViewHeader
100 1 : onClose={props.onClose}
101 1 : canRelocate={canRelocate}
102 1 : migrating={migrating}
103 1 : onRelocate={onRelocate}
104 1 : />
105 1 : <div className={styles.body_content}>
106 1 : <ParamRow label="Backend" value={backendValue} />
107 1 : <ParamRow label="Location" value={schemaValue} />
108 1 : {isNative && location?.nativePath && (
109 0 : <ParamRow label="Folder" value={location.nativePath} />
110 : )}
111 1 : </div>
112 1 : </div>
113 : );
114 1 : };
115 :
116 : type SessionStorageOverlayProps = {
117 : sessionId: string | null;
118 : isOpen: boolean;
119 : onOpen?: () => void;
120 : onClose: () => void;
121 : renderAnchor: (p: object) => React.ReactElement;
122 : side?: AnchorSide;
123 : align?: AnchorAlignment;
124 : anchorOffset?: number;
125 : };
126 :
127 1 : export function SessionStorageOverlay(props: SessionStorageOverlayProps) {
128 1 : return (
129 1 : <AnchoredOverlay
130 1 : open={props.isOpen}
131 1 : onOpen={props.onOpen}
132 1 : onClose={props.onClose}
133 1 : renderAnchor={props.renderAnchor}
134 1 : side={props.side}
135 1 : align={props.align}
136 1 : anchorOffset={props.anchorOffset}
137 1 : overlayProps={{
138 1 : width: OverlaySize.L,
139 1 : height: OverlaySize.S,
140 1 : }}
141 : >
142 1 : <SessionStorageViewer sessionId={props.sessionId} onClose={props.onClose} />
143 1 : </AnchoredOverlay>
144 : );
145 1 : }
|