@putkoff/abstract-securefiles 0.0.48 → 0.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ConfirmButton.d.ts +5 -0
- package/dist/CopyButton.d.ts +4 -0
- package/dist/FolderSelect.d.ts +6 -0
- package/dist/SecureFiles.d.ts +3 -0
- package/dist/ServerFiles.d.ts +6 -0
- package/dist/ShareDialog.d.ts +6 -0
- package/dist/UploadPanel.d.ts +6 -0
- package/dist/api.d.ts +47 -0
- package/dist/index.cjs +529 -1004
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -8
- package/dist/index.js +522 -951
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +60 -0
- package/package.json +15 -11
- package/src/ConfirmButton.tsx +35 -0
- package/src/CopyButton.tsx +24 -0
- package/src/FolderSelect.tsx +68 -0
- package/src/SecureFiles.tsx +346 -0
- package/src/ServerFiles.tsx +339 -0
- package/src/ShareDialog.tsx +148 -0
- package/src/UploadPanel.tsx +217 -0
- package/src/api.ts +108 -0
- package/src/index.ts +4 -8
- package/src/styles.css +442 -0
- package/src/types.ts +56 -0
- package/css.d.ts +0 -2
- package/dist/App/SecureFiles.d.ts +0 -4
- package/dist/App/index.d.ts +0 -1
- package/dist/FileHandlers/FileRow/imports.d.ts +0 -2
- package/dist/FileHandlers/FileRow/index.d.ts +0 -1
- package/dist/FileHandlers/FileRow/src/index.d.ts +0 -1
- package/dist/FileHandlers/FileRow/src/row_utils.d.ts +0 -2
- package/dist/FileHandlers/FileTable/imports.d.ts +0 -4
- package/dist/FileHandlers/FileTable/index.d.ts +0 -1
- package/dist/FileHandlers/FileTable/src/index.d.ts +0 -1
- package/dist/FileHandlers/FileTable/src/table_utils.d.ts +0 -2
- package/dist/FileHandlers/ShareModal/imports.d.ts +0 -2
- package/dist/FileHandlers/ShareModal/index.d.ts +0 -1
- package/dist/FileHandlers/ShareModal/src/index.d.ts +0 -1
- package/dist/FileHandlers/ShareModal/src/share_utils.d.ts +0 -2
- package/dist/FileHandlers/Upload/imports.d.ts +0 -2
- package/dist/FileHandlers/Upload/index.d.ts +0 -1
- package/dist/FileHandlers/Upload/src/index.d.ts +0 -2
- package/dist/FileHandlers/Upload/src/upload_utils.d.ts +0 -2
- package/dist/FileHandlers/imports.d.ts +0 -4
- package/dist/FileHandlers/index.d.ts +0 -4
- package/dist/UI/index.d.ts +0 -28
- package/dist/file_utils/imports.d.ts +0 -2
- package/dist/file_utils/index.d.ts +0 -1
- package/dist/file_utils/src/cookieFetch.d.ts +0 -33
- package/dist/file_utils/src/delete_utils/imports.d.ts +0 -1
- package/dist/file_utils/src/delete_utils/index.d.ts +0 -1
- package/dist/file_utils/src/delete_utils/src/delete_row.d.ts +0 -2
- package/dist/file_utils/src/delete_utils/src/delete_select.d.ts +0 -4
- package/dist/file_utils/src/delete_utils/src/delete_selected.d.ts +0 -2
- package/dist/file_utils/src/delete_utils/src/index.d.ts +0 -3
- package/dist/file_utils/src/download_utils/imports.d.ts +0 -2
- package/dist/file_utils/src/download_utils/index.d.ts +0 -1
- package/dist/file_utils/src/download_utils/src/direct_download.d.ts +0 -3
- package/dist/file_utils/src/download_utils/src/download_file.d.ts +0 -6
- package/dist/file_utils/src/download_utils/src/download_select.d.ts +0 -2
- package/dist/file_utils/src/download_utils/src/index.d.ts +0 -5
- package/dist/file_utils/src/download_utils/src/target_downloads.d.ts +0 -4
- package/dist/file_utils/src/download_utils/src/trigger_download.d.ts +0 -2
- package/dist/file_utils/src/imports.d.ts +0 -2
- package/dist/file_utils/src/index.d.ts +0 -6
- package/dist/file_utils/src/list_utils/imports.d.ts +0 -1
- package/dist/file_utils/src/list_utils/index.d.ts +0 -1
- package/dist/file_utils/src/list_utils/src/index.d.ts +0 -1
- package/dist/file_utils/src/list_utils/src/list_utils.d.ts +0 -2
- package/dist/file_utils/src/select_utils/index.d.ts +0 -1
- package/dist/file_utils/src/select_utils/src/index.d.ts +0 -3
- package/dist/file_utils/src/select_utils/src/init_select_all.d.ts +0 -2
- package/dist/file_utils/src/select_utils/src/sync_header_checkbox.d.ts +0 -5
- package/dist/file_utils/src/select_utils/src/toggle_checkboxes.d.ts +0 -2
- package/dist/file_utils/src/share_utils/imports.d.ts +0 -1
- package/dist/file_utils/src/share_utils/index.d.ts +0 -1
- package/dist/file_utils/src/share_utils/src/index.d.ts +0 -1
- package/dist/file_utils/src/share_utils/src/share_utils.d.ts +0 -7
- package/dist/file_utils/src/upload_utils/imports.d.ts +0 -0
- package/dist/file_utils/src/upload_utils/index.d.ts +0 -1
- package/dist/file_utils/src/upload_utils/src/index.d.ts +0 -3
- package/dist/file_utils/src/upload_utils/src/patch_file_settings.d.ts +0 -2
- package/dist/file_utils/src/upload_utils/src/upload_file.d.ts +0 -2
- package/dist/file_utils/src/upload_utils/src/upload_files.d.ts +0 -5
- package/dist/hooks/imports.d.ts +0 -3
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/useFiles/imports.d.ts +0 -2
- package/dist/hooks/useFiles/index.d.ts +0 -1
- package/dist/hooks/useFiles/src/deleteFile.d.ts +0 -3
- package/dist/hooks/useFiles/src/index.d.ts +0 -6
- package/dist/hooks/useFiles/src/load.d.ts +0 -3
- package/dist/hooks/useFiles/src/patchShareSettings.d.ts +0 -3
- package/dist/hooks/useFiles/src/toggleShare.d.ts +0 -3
- package/dist/hooks/useFiles/src/useFiles.d.ts +0 -13
- package/dist/hooks/useFiles/src/useSaveShareSettings.d.ts +0 -7
- package/dist/imports.d.ts +0 -6
- package/dist/index.css +0 -1
- package/dist/styles/index.d.ts +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/types.d.ts +0 -75
- package/index.ts +0 -1
- package/postcss.config.cjs +0 -8
- package/rollup.config.js +0 -38
- package/rollup.config.mjs +0 -35
- package/src/App/SecureFiles.tsx +0 -45
- package/src/App/index.ts +0 -1
- package/src/FileHandlers/FileRow/imports.ts +0 -2
- package/src/FileHandlers/FileRow/index.ts +0 -1
- package/src/FileHandlers/FileRow/src/index.ts +0 -1
- package/src/FileHandlers/FileRow/src/row_utils.tsx +0 -143
- package/src/FileHandlers/FileTable/imports.ts +0 -15
- package/src/FileHandlers/FileTable/index.ts +0 -1
- package/src/FileHandlers/FileTable/src/index.ts +0 -1
- package/src/FileHandlers/FileTable/src/table_utils.tsx +0 -251
- package/src/FileHandlers/ShareModal/imports.ts +0 -2
- package/src/FileHandlers/ShareModal/index.ts +0 -1
- package/src/FileHandlers/ShareModal/src/index.ts +0 -1
- package/src/FileHandlers/ShareModal/src/share_utils.tsx +0 -155
- package/src/FileHandlers/Upload/imports.ts +0 -3
- package/src/FileHandlers/Upload/index.ts +0 -1
- package/src/FileHandlers/Upload/src/index.ts +0 -2
- package/src/FileHandlers/Upload/src/upload_utils.tsx +0 -369
- package/src/FileHandlers/imports.ts +0 -17
- package/src/FileHandlers/index.ts +0 -4
- package/src/UI/index.tsx +0 -122
- package/src/custom.d.ts +0 -7
- package/src/file_utils/imports.ts +0 -2
- package/src/file_utils/index.ts +0 -1
- package/src/file_utils/src/cookieFetch.ts +0 -109
- package/src/file_utils/src/delete_utils/imports.ts +0 -1
- package/src/file_utils/src/delete_utils/index.ts +0 -1
- package/src/file_utils/src/delete_utils/src/delete_row.ts +0 -11
- package/src/file_utils/src/delete_utils/src/delete_select.ts +0 -10
- package/src/file_utils/src/delete_utils/src/delete_selected.ts +0 -15
- package/src/file_utils/src/delete_utils/src/index.ts +0 -3
- package/src/file_utils/src/download_utils/imports.ts +0 -3
- package/src/file_utils/src/download_utils/index.ts +0 -1
- package/src/file_utils/src/download_utils/src/direct_download.ts +0 -22
- package/src/file_utils/src/download_utils/src/download_file.ts +0 -25
- package/src/file_utils/src/download_utils/src/download_select.ts +0 -25
- package/src/file_utils/src/download_utils/src/index.ts +0 -5
- package/src/file_utils/src/download_utils/src/target_downloads.ts +0 -21
- package/src/file_utils/src/download_utils/src/trigger_download.ts +0 -14
- package/src/file_utils/src/imports.ts +0 -3
- package/src/file_utils/src/index.ts +0 -6
- package/src/file_utils/src/list_utils/imports.ts +0 -2
- package/src/file_utils/src/list_utils/index.ts +0 -1
- package/src/file_utils/src/list_utils/src/index.ts +0 -1
- package/src/file_utils/src/list_utils/src/list_utils.ts +0 -28
- package/src/file_utils/src/select_utils/index.ts +0 -1
- package/src/file_utils/src/select_utils/src/index.ts +0 -3
- package/src/file_utils/src/select_utils/src/init_select_all.ts +0 -37
- package/src/file_utils/src/select_utils/src/sync_header_checkbox.ts +0 -18
- package/src/file_utils/src/select_utils/src/toggle_checkboxes.ts +0 -11
- package/src/file_utils/src/share_utils/imports.ts +0 -1
- package/src/file_utils/src/share_utils/index.ts +0 -1
- package/src/file_utils/src/share_utils/src/index.ts +0 -1
- package/src/file_utils/src/share_utils/src/share_utils.ts +0 -20
- package/src/file_utils/src/upload_utils/imports.ts +0 -1
- package/src/file_utils/src/upload_utils/index.ts +0 -1
- package/src/file_utils/src/upload_utils/src/index.ts +0 -3
- package/src/file_utils/src/upload_utils/src/patch_file_settings.ts +0 -20
- package/src/file_utils/src/upload_utils/src/upload_file.ts +0 -27
- package/src/file_utils/src/upload_utils/src/upload_files.ts +0 -19
- package/src/hooks/imports.ts +0 -3
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useFiles/imports.ts +0 -3
- package/src/hooks/useFiles/index.ts +0 -1
- package/src/hooks/useFiles/src/deleteFile.ts +0 -23
- package/src/hooks/useFiles/src/index.ts +0 -6
- package/src/hooks/useFiles/src/load.ts +0 -39
- package/src/hooks/useFiles/src/logs/clipit_logs.log +0 -0
- package/src/hooks/useFiles/src/logs/routes_bp.log +0 -0
- package/src/hooks/useFiles/src/patchShareSettings.ts +0 -27
- package/src/hooks/useFiles/src/toggleShare.ts +0 -28
- package/src/hooks/useFiles/src/useFiles.ts +0 -35
- package/src/hooks/useFiles/src/useSaveShareSettings.ts +0 -31
- package/src/imports.ts +0 -18
- package/src/styles/SecureFiles/App.css +0 -42
- package/src/styles/SecureFiles/index.css +0 -10
- package/src/styles/SecureFiles/logs/clipit_logs.log +0 -0
- package/src/styles/SecureFiles/logs/routes_bp.log +0 -0
- package/src/styles/SecureFiles/tailwind.css +0 -3
- package/src/styles/SecureFiles/window-manager.css +0 -283
- package/src/styles/SecureFilesStyles.module.css +0 -287
- package/src/styles/index.ts +0 -1
- package/src/types/index.ts +0 -1
- package/src/types/src/index.ts +0 -1
- package/src/types/src/types.ts +0 -88
- package/tailwind.config.cjs +0 -7
- package/testit.mjs +0 -11
- package/testit.ts +0 -3
- package/tsconfig.json +0 -27
- /package/src/{global.d.ts → css.d.ts} +0 -0
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import type { FileItem, FileTableProps } from './../imports';
|
|
2
|
-
import React, { useState, useMemo, ChangeEvent } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
useFiles,
|
|
5
|
-
downloadFile,
|
|
6
|
-
Spinner,
|
|
7
|
-
ShareModal,
|
|
8
|
-
Input,
|
|
9
|
-
Button,
|
|
10
|
-
currentUsername
|
|
11
|
-
} from './../imports';
|
|
12
|
-
import { downloadSelected } from './../../../file_utils';
|
|
13
|
-
|
|
14
|
-
// Tree node type
|
|
15
|
-
interface FileNode {
|
|
16
|
-
name: string;
|
|
17
|
-
path: string;
|
|
18
|
-
children: Map<string, FileNode>;
|
|
19
|
-
files: FileItem[];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Build a directory tree from a flat file list
|
|
23
|
-
function buildTree(files: FileItem[]): FileNode {
|
|
24
|
-
const root: FileNode = { name: '', path: '', children: new Map(), files: [] };
|
|
25
|
-
files.forEach(file => {
|
|
26
|
-
const parts = file.filepath.split('/');
|
|
27
|
-
let node = root;
|
|
28
|
-
parts.slice(0, -1).forEach((seg, idx) => {
|
|
29
|
-
const subPath = parts.slice(0, idx + 1).join('/');
|
|
30
|
-
if (!node.children.has(seg)) {
|
|
31
|
-
node.children.set(seg, { name: seg, path: subPath, children: new Map(), files: [] });
|
|
32
|
-
}
|
|
33
|
-
node = node.children.get(seg)!;
|
|
34
|
-
});
|
|
35
|
-
node.files.push(file);
|
|
36
|
-
});
|
|
37
|
-
return root;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Collect all file IDs under a node (recursive)
|
|
41
|
-
function collectNodeIds(node: FileNode): Array<number | string> {
|
|
42
|
-
let ids: Array<number | string> = [];
|
|
43
|
-
node.files.forEach(f => ids.push(f.id));
|
|
44
|
-
node.children.forEach(child => {
|
|
45
|
-
ids = ids.concat(collectNodeIds(child));
|
|
46
|
-
});
|
|
47
|
-
return ids;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export default function FileTable({
|
|
51
|
-
files,
|
|
52
|
-
loading,
|
|
53
|
-
error,
|
|
54
|
-
onToggleShare,
|
|
55
|
-
onPatchShare,
|
|
56
|
-
onDelete
|
|
57
|
-
}: FileTableProps) {
|
|
58
|
-
const { load } = useFiles();
|
|
59
|
-
const [search, setSearch] = useState('');
|
|
60
|
-
const [modalFile, setModalFile] = useState<FileItem | null>(null);
|
|
61
|
-
const [selected, setSelected] = useState<Set<number | string>>(new Set());
|
|
62
|
-
const [expanded, setExpanded] = useState<Set<string>>(new Set());
|
|
63
|
-
|
|
64
|
-
// Upload controls
|
|
65
|
-
const [uploadDir, setUploadDir] = useState<string>('');
|
|
66
|
-
const [uploadFile, setUploadFile] = useState<File | null>(null);
|
|
67
|
-
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => setUploadFile(e.target.files?.[0] ?? null);
|
|
68
|
-
const handleUpload = async () => {
|
|
69
|
-
if (!uploadFile) return;
|
|
70
|
-
const form = new FormData();
|
|
71
|
-
form.append('file', uploadFile);
|
|
72
|
-
const dirPart = uploadDir ? `${uploadDir}/` : '';
|
|
73
|
-
const url = `/api/secure-files/upload/${dirPart}${encodeURIComponent(uploadFile.name)}`;
|
|
74
|
-
await fetch(url, { method: 'POST', credentials: 'include', body: form });
|
|
75
|
-
setUploadFile(null);
|
|
76
|
-
load();
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// Filter files by search term
|
|
80
|
-
const filtered = useMemo(
|
|
81
|
-
() => files.filter(f => f.filename.toLowerCase().includes(search.trim().toLowerCase())),
|
|
82
|
-
[files, search]
|
|
83
|
-
);
|
|
84
|
-
// Build tree and directory paths list
|
|
85
|
-
const tree = useMemo(() => buildTree(filtered), [filtered]);
|
|
86
|
-
const allDirPaths = useMemo(() => {
|
|
87
|
-
const paths: string[] = [''];
|
|
88
|
-
function collect(node: FileNode) {
|
|
89
|
-
node.children.forEach(child => {
|
|
90
|
-
paths.push(child.path);
|
|
91
|
-
collect(child);
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
collect(tree);
|
|
95
|
-
return paths;
|
|
96
|
-
}, [tree]);
|
|
97
|
-
|
|
98
|
-
// Selection & expand/collapse logic
|
|
99
|
-
const allIds = filtered.map(f => f.id);
|
|
100
|
-
const toggleAllSelection = () => selected.size < allIds.length ? setSelected(new Set(allIds)) : setSelected(new Set());
|
|
101
|
-
const toggleDirOpen = (path: string) => setExpanded(prev => {
|
|
102
|
-
const next = new Set(prev);
|
|
103
|
-
next.has(path) ? next.delete(path) : next.add(path);
|
|
104
|
-
return next;
|
|
105
|
-
});
|
|
106
|
-
const expandAll = () => setExpanded(new Set(allDirPaths));
|
|
107
|
-
const collapseAll = () => setExpanded(new Set());
|
|
108
|
-
|
|
109
|
-
// Recursive renderer: directory row with select-all checkbox, then its files, then subdirectories
|
|
110
|
-
const renderNode = (node: FileNode, depth = 0): React.ReactNode[] => {
|
|
111
|
-
const rows: React.ReactNode[] = [];
|
|
112
|
-
if (node.path) {
|
|
113
|
-
const isOpen = expanded.has(node.path);
|
|
114
|
-
const nodeIds = collectNodeIds(node);
|
|
115
|
-
const allSelected = nodeIds.every(id => selected.has(id));
|
|
116
|
-
const someSelected = nodeIds.some(id => selected.has(id));
|
|
117
|
-
rows.push(
|
|
118
|
-
<tr key={node.path}>
|
|
119
|
-
{/* Directory-level checkbox */}
|
|
120
|
-
<td className="px-4 py-2 bg-gray-100">
|
|
121
|
-
<input
|
|
122
|
-
type="checkbox"
|
|
123
|
-
checked={allSelected}
|
|
124
|
-
ref={el => { if (el) el.indeterminate = !allSelected && someSelected; }}
|
|
125
|
-
onChange={() => {
|
|
126
|
-
setSelected(prev => {
|
|
127
|
-
const next = new Set(prev);
|
|
128
|
-
if (allSelected) {
|
|
129
|
-
nodeIds.forEach(id => next.delete(id));
|
|
130
|
-
} else {
|
|
131
|
-
nodeIds.forEach(id => next.add(id));
|
|
132
|
-
}
|
|
133
|
-
return next;
|
|
134
|
-
});
|
|
135
|
-
}}
|
|
136
|
-
/>
|
|
137
|
-
</td>
|
|
138
|
-
{/* Directory label pushed left */}
|
|
139
|
-
<td className="px-4 py-2 bg-gray-100 font-bold" colSpan={5} style={{ textAlign: 'left' }}>
|
|
140
|
-
{node.path}
|
|
141
|
-
</td>
|
|
142
|
-
{/* Expand/collapse ticker on right */}
|
|
143
|
-
<td className="px-4 py-2 bg-gray-100 text-right">
|
|
144
|
-
<button onClick={() => toggleDirOpen(node.path)}>
|
|
145
|
-
{isOpen ? '▾' : '▸'}
|
|
146
|
-
</button>
|
|
147
|
-
</td>
|
|
148
|
-
</tr>
|
|
149
|
-
);
|
|
150
|
-
if (!isOpen) return rows;
|
|
151
|
-
}
|
|
152
|
-
// Render files in this directory
|
|
153
|
-
node.files.sort((a, b) => a.filename.localeCompare(b.filename)).forEach(f => {
|
|
154
|
-
const isOwner = f.uploader_id.toLowerCase() === (currentUsername() ?? '').toLowerCase();
|
|
155
|
-
const canDownload = isOwner || f.shareable;
|
|
156
|
-
rows.push(
|
|
157
|
-
<tr key={f.id}>
|
|
158
|
-
<td className="px-4 py-2">
|
|
159
|
-
<input
|
|
160
|
-
type="checkbox"
|
|
161
|
-
checked={selected.has(f.id)}
|
|
162
|
-
onChange={() => setSelected(prev => {
|
|
163
|
-
const next = new Set(prev);
|
|
164
|
-
next.has(f.id) ? next.delete(f.id) : next.add(f.id);
|
|
165
|
-
return next;
|
|
166
|
-
})}
|
|
167
|
-
/>
|
|
168
|
-
</td>
|
|
169
|
-
<td className="px-4 py-2" style={{ paddingLeft: (depth + 1) * 20 }}>
|
|
170
|
-
{f.filename}
|
|
171
|
-
</td>
|
|
172
|
-
<td className="px-4 py-2">
|
|
173
|
-
{canDownload ? <Button variant="secondary" onClick={() => downloadFile(f)}>Download</Button> : <span>🔒</span>}
|
|
174
|
-
</td>
|
|
175
|
-
<td className="px-4 py-2">
|
|
176
|
-
{isOwner && <Button variant="icon" onClick={() => setModalFile(f)}>⚙️</Button>}
|
|
177
|
-
</td>
|
|
178
|
-
<td className="px-4 py-2">
|
|
179
|
-
{isOwner && <Button variant={f.shareable ? 'primary' : 'secondary'} onClick={() => onToggleShare(f)}>{f.shareable ? 'Sharing' : 'Share'}</Button>}
|
|
180
|
-
</td>
|
|
181
|
-
<td className="px-4 py-2">
|
|
182
|
-
{isOwner && <Button variant="icon" onClick={() => onDelete(f).then(load)}>🗑</Button>}
|
|
183
|
-
</td>
|
|
184
|
-
<td className="px-4 py-2">
|
|
185
|
-
{f.download_count} / {f.download_limit ?? '∞'}
|
|
186
|
-
</td>
|
|
187
|
-
</tr>
|
|
188
|
-
);
|
|
189
|
-
});
|
|
190
|
-
// Render subdirectories
|
|
191
|
-
Array.from(node.children.values())
|
|
192
|
-
.sort((a, b) => a.name.localeCompare(b.name))
|
|
193
|
-
.forEach(child => rows.push(...renderNode(child, depth + 1)));
|
|
194
|
-
return rows;
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// Batch actions
|
|
198
|
-
const handleDownloadAll = async () => {
|
|
199
|
-
const inputs = filtered.filter(f => selected.has(f.id)).map(f => document.createElement('input'));
|
|
200
|
-
inputs.forEach((cb, idx) => cb.value = filtered.filter(f => selected.has(f.id))[idx].filepath);
|
|
201
|
-
await downloadSelected(inputs);
|
|
202
|
-
};
|
|
203
|
-
const handleDeleteAll = async () => { await onDelete(filtered.filter(f => selected.has(f.id))); setSelected(new Set()); await load(); };
|
|
204
|
-
const handleShareAll = async () => { for (const f of filtered.filter(f => selected.has(f.id))) await onToggleShare(f); };
|
|
205
|
-
|
|
206
|
-
if (loading) return <Spinner />;
|
|
207
|
-
if (error) return <p className="text-red-600">{error.message}</p>;
|
|
208
|
-
|
|
209
|
-
return (
|
|
210
|
-
<>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
{/* Search & actions */}
|
|
214
|
-
<div className="mb-4 flex flex-col md:flex-row md:justify-between gap-4">
|
|
215
|
-
<Input label="Search files" type="text" value={search} onChange={(e:any) => setSearch(e.target.value)} placeholder="Enter filename…" className="form-input flex-1" />
|
|
216
|
-
<div className="flex gap-2">
|
|
217
|
-
<Button onClick={expandAll} disabled={!allDirPaths.length}>Expand All</Button>
|
|
218
|
-
<Button onClick={collapseAll} disabled={!allDirPaths.length}>Collapse All</Button>
|
|
219
|
-
<Button onClick={handleDownloadAll} disabled={!selected.size}>Download Selected</Button>
|
|
220
|
-
<Button color="gray" onClick={handleDeleteAll} disabled={!selected.size}>Delete Selected</Button>
|
|
221
|
-
<Button onClick={handleShareAll} disabled={!selected.size}>Share Selected</Button>
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
|
|
225
|
-
{/* File tree table */}
|
|
226
|
-
<div className="overflow-x-auto wm-container">
|
|
227
|
-
<table className="file-table w-full">
|
|
228
|
-
<thead className="wm-thead">
|
|
229
|
-
<tr>
|
|
230
|
-
<th className="px-4 py-2 w-12"><input type="checkbox" checked={selected.size === filtered.length && filtered.length > 0} onChange={toggleAllSelection} /></th>
|
|
231
|
-
<th className="px-4 py-2">Filename</th>
|
|
232
|
-
<th className="px-4 py-2">Download</th>
|
|
233
|
-
<th className="px-4 py-2">Settings</th>
|
|
234
|
-
<th className="px-4 py-2">Share</th>
|
|
235
|
-
<th className="px-4 py-2">Delete</th>
|
|
236
|
-
<th className="px-4 py-2">Downloads</th>
|
|
237
|
-
</tr>
|
|
238
|
-
</thead>
|
|
239
|
-
<tbody>{renderNode(tree)}</tbody>
|
|
240
|
-
</table>
|
|
241
|
-
</div>
|
|
242
|
-
|
|
243
|
-
<ShareModal file={modalFile} onClose={() => setModalFile(null)} onSave={async patch => {
|
|
244
|
-
if (!modalFile) return;
|
|
245
|
-
const updated: FileItem = { ...modalFile, ...patch };
|
|
246
|
-
await onPatchShare(updated);
|
|
247
|
-
setModalFile(null);
|
|
248
|
-
}} />
|
|
249
|
-
</>
|
|
250
|
-
);
|
|
251
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {ShareModal} from './src';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {default as ShareModal} from './share_utils';
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ShareModalProps
|
|
3
|
-
} from './../../imports';
|
|
4
|
-
import {
|
|
5
|
-
Button,
|
|
6
|
-
Checkbox,
|
|
7
|
-
Input,
|
|
8
|
-
ClipboardIcon,
|
|
9
|
-
createPortal,
|
|
10
|
-
useEffect,
|
|
11
|
-
useRef,
|
|
12
|
-
useState,
|
|
13
|
-
} from './../imports';
|
|
14
|
-
|
|
15
|
-
export default function ShareModal({ file, onClose, onSave }: ShareModalProps) {
|
|
16
|
-
const [fileId, setFileId] = useState<string | number | "">("");
|
|
17
|
-
const [enabled, setEnabled] = useState<any>(false);
|
|
18
|
-
const [password, setPassword] = useState<any>(undefined);
|
|
19
|
-
const [strPassword, setStrPassword] = useState<any>("");
|
|
20
|
-
const [download_limit, setDownloadLimit] = useState<number | "">(0);
|
|
21
|
-
const [error, setError] = useState("");
|
|
22
|
-
const [busy, setBusy] = useState(false);
|
|
23
|
-
const modalRef = useRef<HTMLDivElement>(null);
|
|
24
|
-
|
|
25
|
-
const shareLink =
|
|
26
|
-
file && enabled
|
|
27
|
-
? `/api/secure-files/secure-download/${file.id}`
|
|
28
|
-
: "";
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
if (!file) return;
|
|
32
|
-
setFileId(file.id);
|
|
33
|
-
setEnabled(file.shareable);
|
|
34
|
-
setPassword(file.share_password);
|
|
35
|
-
setStrPassword(file.password_str ?? "");
|
|
36
|
-
setDownloadLimit(file.download_limit ?? 0);
|
|
37
|
-
setError("");
|
|
38
|
-
|
|
39
|
-
modalRef.current?.focus();
|
|
40
|
-
}, [file]);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
const h = (e: KeyboardEvent) => e.key === "Escape" && onClose();
|
|
45
|
-
window.addEventListener("keydown", h);
|
|
46
|
-
return () => window.removeEventListener("keydown", h);
|
|
47
|
-
}, [onClose]);
|
|
48
|
-
|
|
49
|
-
if (!file) return null;
|
|
50
|
-
|
|
51
|
-
async function handleSave() {
|
|
52
|
-
if (strPassword && strPassword.length < 4) {
|
|
53
|
-
setError("Password must be at least 4 characters");
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
setBusy(true);
|
|
57
|
-
const save_dict = {
|
|
58
|
-
id: fileId,
|
|
59
|
-
shareable: enabled,
|
|
60
|
-
needsPassword: !!strPassword,
|
|
61
|
-
share_password: strPassword || null,
|
|
62
|
-
password_str: strPassword || null,
|
|
63
|
-
download_limit: download_limit === "" ? null : Number(download_limit),
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const resp = await onSave(save_dict);
|
|
68
|
-
|
|
69
|
-
onClose();
|
|
70
|
-
} catch (err) {
|
|
71
|
-
setError(`${err} Failed to save settings. Please try again.`);
|
|
72
|
-
} finally {
|
|
73
|
-
setBusy(false);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return createPortal(
|
|
78
|
-
<>
|
|
79
|
-
<div
|
|
80
|
-
className="modal-backdrop z-100"
|
|
81
|
-
onClick={(e) => e.target === e.currentTarget && onClose()}
|
|
82
|
-
/>
|
|
83
|
-
<div className="modal-wrapper z-110">
|
|
84
|
-
<div
|
|
85
|
-
ref={modalRef}
|
|
86
|
-
role="dialog"
|
|
87
|
-
aria-modal="true"
|
|
88
|
-
tabIndex={-1}
|
|
89
|
-
className="modal-panel"
|
|
90
|
-
>
|
|
91
|
-
<h3 className="form-title">
|
|
92
|
-
Share settings for <span className="font-mono">{file.filename}</span>
|
|
93
|
-
</h3>
|
|
94
|
-
|
|
95
|
-
{error && <p className="form-error">{error}</p>}
|
|
96
|
-
|
|
97
|
-
<Checkbox
|
|
98
|
-
label="Enable sharing"
|
|
99
|
-
checked={enabled}
|
|
100
|
-
onChange={(e:any) => setEnabled(e.target.checked)}
|
|
101
|
-
className="checkbox"
|
|
102
|
-
/>
|
|
103
|
-
|
|
104
|
-
<Input
|
|
105
|
-
label="Password (optional)"
|
|
106
|
-
type="text"
|
|
107
|
-
value={strPassword}
|
|
108
|
-
onChange={(e:any) => setStrPassword(e.target.value)}
|
|
109
|
-
disabled={!enabled}
|
|
110
|
-
className="form-input"
|
|
111
|
-
/>
|
|
112
|
-
<Input
|
|
113
|
-
label="Download limit (0 = ∞)"
|
|
114
|
-
type="number"
|
|
115
|
-
min={0}
|
|
116
|
-
value={download_limit}
|
|
117
|
-
onChange={(e:any) =>
|
|
118
|
-
setDownloadLimit(e.target.value === "" ? "" : Number(e.target.value))
|
|
119
|
-
}
|
|
120
|
-
disabled={!enabled}
|
|
121
|
-
className="form-input"
|
|
122
|
-
/>
|
|
123
|
-
|
|
124
|
-
{enabled && shareLink && (
|
|
125
|
-
<Input
|
|
126
|
-
label="Share link"
|
|
127
|
-
readOnly
|
|
128
|
-
value={shareLink}
|
|
129
|
-
className="form-input"
|
|
130
|
-
trailing={
|
|
131
|
-
<Button
|
|
132
|
-
variant="icon"
|
|
133
|
-
className="btn-icon"
|
|
134
|
-
onClick={() => navigator.clipboard.writeText(shareLink)}
|
|
135
|
-
>
|
|
136
|
-
<ClipboardIcon className="h-5 w-5" />
|
|
137
|
-
</Button>
|
|
138
|
-
}
|
|
139
|
-
/>
|
|
140
|
-
)}
|
|
141
|
-
|
|
142
|
-
<div className="mt-6 flex justify-end gap-2">
|
|
143
|
-
<Button color="green" variant="primary" onClick={handleSave} disabled={busy} className="btn-primary">
|
|
144
|
-
{busy ? "Saving…" : "Save"}
|
|
145
|
-
</Button>
|
|
146
|
-
<Button variant="secondary" onClick={onClose} disabled={busy} className="btn-secondary">
|
|
147
|
-
Cancel
|
|
148
|
-
</Button>
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
</>,
|
|
153
|
-
document.body
|
|
154
|
-
);
|
|
155
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {Upload} from './src';
|