Skip to content

Commit 6b88cea

Browse files
Merge pull request #12 from solid/feat/my-storages
feat: preview file
2 parents b162826 + b72366d commit 6b88cea

8 files changed

Lines changed: 390 additions & 20 deletions

File tree

app/components/FileItem.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ interface FileItemProps {
2222
onSelect: (file: FileItemData) => void;
2323
onDoubleClick: (file: FileItemData) => void;
2424
onRename?: (file: FileItemData) => void;
25+
onPreview?: (file: FileItemData) => void;
2526
isSelected?: boolean;
2627
}
2728

@@ -31,6 +32,7 @@ export default function FileItem({
3132
onSelect,
3233
onDoubleClick,
3334
onRename,
35+
onPreview,
3436
isSelected = false,
3537
}: FileItemProps) {
3638
const [isHovered, setIsHovered] = useState(false);
@@ -124,6 +126,7 @@ export default function FileItem({
124126
file={file}
125127
position="top-right"
126128
onRename={onRename}
129+
onPreview={onPreview}
127130
onDownload={(f) => console.log("Download:", f.name)}
128131
onCopy={(f) => console.log("Copy:", f.name)}
129132
onMove={(f) => console.log("Move:", f.name)}
@@ -171,6 +174,7 @@ export default function FileItem({
171174
file={file}
172175
position="right"
173176
onRename={onRename}
177+
onPreview={onPreview}
174178
onDownload={(f) => console.log("Download:", f.name)}
175179
onCopy={(f) => console.log("Copy:", f.name)}
176180
onMove={(f) => console.log("Move:", f.name)}

app/components/FileItemMenu.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
DocumentDuplicateIcon,
1010
ArrowRightCircleIcon,
1111
TrashIcon,
12+
EyeIcon,
1213
} from "@heroicons/react/24/outline";
1314
import { useClickOutside } from "../lib/hooks";
1415
import { FileItemData } from "./FileItem";
@@ -20,6 +21,7 @@ interface FileItemMenuProps {
2021
onCopy?: (file: FileItemData) => void;
2122
onMove?: (file: FileItemData) => void;
2223
onDelete?: (file: FileItemData) => void;
24+
onPreview?: (file: FileItemData) => void;
2325
position?: "top-right" | "right";
2426
}
2527

@@ -30,6 +32,7 @@ export default function FileItemMenu({
3032
onCopy,
3133
onMove,
3234
onDelete,
35+
onPreview,
3336
position = "right",
3437
}: FileItemMenuProps) {
3538
const [showMenu, setShowMenu] = useState(false);
@@ -68,6 +71,18 @@ export default function FileItemMenu({
6871
};
6972

7073
const menuItems = [
74+
// Only show Preview for files, not folders
75+
...(file.type === "file"
76+
? [
77+
{
78+
label: "Preview",
79+
icon: EyeIcon,
80+
action: onPreview,
81+
className: "text-gray-700 hover:bg-gray-100 border-b border-gray-100",
82+
iconClassName: "text-gray-500",
83+
},
84+
]
85+
: []),
7186
{
7287
label: "Rename",
7388
icon: PencilIcon,

app/components/FileList.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ interface FileListProps {
1111
onFileSelect: (file: FileItemData) => void;
1212
onFileDoubleClick: (file: FileItemData) => void;
1313
onFileRename?: (file: FileItemData) => void;
14+
onFilePreview?: (file: FileItemData) => void;
1415
selectedFileIds: string[];
1516
}
1617

@@ -22,6 +23,7 @@ export default function FileList({
2223
onFileSelect,
2324
onFileDoubleClick,
2425
onFileRename,
26+
onFilePreview,
2527
selectedFileIds,
2628
}: FileListProps) {
2729
const [view, setView] = useState<"grid" | "list">(() => {
@@ -56,6 +58,7 @@ export default function FileList({
5658
onSelect={onFileSelect}
5759
onDoubleClick={onFileDoubleClick}
5860
onRename={onFileRename}
61+
onPreview={onFilePreview}
5962
isSelected={selectedFileIds.includes(file.id)}
6063
/>
6164
))}
@@ -70,6 +73,7 @@ export default function FileList({
7073
onSelect={onFileSelect}
7174
onDoubleClick={onFileDoubleClick}
7275
onRename={onFileRename}
76+
onPreview={onFilePreview}
7377
isSelected={selectedFileIds.includes(file.id)}
7478
/>
7579
))}

app/components/FileManager.tsx

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import FileList from "./FileList";
1010
import PermissionsDialog, { Permission } from "./PermissionsDialog";
1111
import NewFolderDialog from "./NewFolderDialog";
1212
import RenameDialog from "./RenameDialog";
13+
import PreviewModal from "./PreviewModal";
1314
import FileUploadHandler from "./FileUploadHandler";
1415
import { FileItemData } from "./FileItem";
1516
import { useSolidStorages, useBrowseStorage } from "../lib/hooks";
@@ -27,12 +28,12 @@ export default function FileManager() {
2728
const [isInitialized, setIsInitialized] = useState(false);
2829
const [savedUrl, setSavedUrl] = useState<string | null>(() => {
2930
if (typeof window === "undefined") return null;
30-
31+
3132
try {
3233
const fullUrl = window.location.href;
3334
const urlObj = new URL(fullUrl);
3435
let urlParam = urlObj.searchParams.get("url");
35-
36+
3637
if (urlParam) {
3738
try {
3839
urlParam = decodeURIComponent(urlParam);
@@ -41,7 +42,7 @@ export default function FileManager() {
4142
}
4243
return urlParam;
4344
}
44-
45+
4546
const stored = sessionStorage.getItem("solid-file-manager-url");
4647
if (stored) {
4748
return stored;
@@ -54,11 +55,11 @@ export default function FileManager() {
5455

5556
useEffect(() => {
5657
if (typeof window === "undefined") return;
57-
58+
5859
const fullUrl = window.location.href;
5960
const urlObj = new URL(fullUrl);
6061
let urlParam = urlObj.searchParams.get("url");
61-
62+
6263
if (urlParam) {
6364
try {
6465
urlParam = decodeURIComponent(urlParam);
@@ -80,22 +81,22 @@ export default function FileManager() {
8081

8182
const restoreFromUrl = () => {
8283
let urlParam: string | null = savedUrl;
83-
84+
8485
if (!urlParam && typeof window !== "undefined") {
8586
const fullUrl = window.location.href;
8687
const urlObj = new URL(fullUrl);
8788
urlParam = urlObj.searchParams.get("url");
88-
89+
8990
if (!urlParam && window.location.search) {
9091
const urlParams = new URLSearchParams(window.location.search);
9192
urlParam = urlParams.get("url");
9293
}
9394
}
94-
95+
9596
if (!urlParam) {
9697
urlParam = searchParams.get("url");
9798
}
98-
99+
99100
if (urlParam) {
100101
try {
101102
let decodedUrl = urlParam;
@@ -104,7 +105,7 @@ export default function FileManager() {
104105
} catch (e) {
105106
decodedUrl = urlParam;
106107
}
107-
108+
108109
const matchingStorage = storages.find((s) => {
109110
const storageUrl = s.url.endsWith("/") ? s.url : s.url + "/";
110111
const normalizedDecoded = decodedUrl.endsWith("/") ? decodedUrl : decodedUrl + "/";
@@ -114,29 +115,29 @@ export default function FileManager() {
114115

115116
if (matchingStorage) {
116117
setSelectedStorageId(matchingStorage.id);
117-
118+
118119
if (decodedUrl === matchingStorage.url || decodedUrl === matchingStorage.url + "/") {
119120
setCurrentPath("/");
120121
} else {
121122
setCurrentPath(decodedUrl);
122123
}
123-
124+
124125
if (typeof window !== "undefined") {
125126
const encodedUrl = encodeURIComponent(decodedUrl);
126127
const params = new URLSearchParams();
127128
params.set("url", encodedUrl);
128129
const newUrl = `/?${params.toString()}`;
129130
router.replace(newUrl, { scroll: false });
130131
}
131-
132+
132133
setIsInitialized(true);
133134
return;
134135
}
135136
} catch (e) {
136137
// Ignore errors
137138
}
138139
}
139-
140+
140141
setIsInitialized(true);
141142
};
142143

@@ -165,11 +166,11 @@ export default function FileManager() {
165166
const params = new URLSearchParams();
166167
params.set("url", encodedUrl);
167168
const newUrl = `/?${params.toString()}`;
168-
169+
169170
if (typeof window !== "undefined") {
170171
sessionStorage.setItem("solid-file-manager-url", urlToEncode);
171172
}
172-
173+
173174
router.replace(newUrl, { scroll: false });
174175
};
175176

@@ -178,7 +179,7 @@ export default function FileManager() {
178179
? storages.find((s) => s.id === selectedStorageId)?.url || null
179180
: currentPath
180181
: null;
181-
182+
182183
const [refreshKey, setRefreshKey] = useState(0);
183184
const { files: browsedFiles, isLoading: isLoadingFiles, error: browseError } = useBrowseStorage(containerUrlToBrowse, refreshKey);
184185
const [permissionsDialogOpen, setPermissionsDialogOpen] = useState(false);
@@ -190,6 +191,8 @@ export default function FileManager() {
190191
const [fileUploadTrigger, setFileUploadTrigger] = useState(0);
191192
const [showRenameDialog, setShowRenameDialog] = useState(false);
192193
const [fileToRename, setFileToRename] = useState<FileItemData | null>(null);
194+
const [showPreviewModal, setShowPreviewModal] = useState(false);
195+
const [fileToPreview, setFileToPreview] = useState<FileItemData | null>(null);
193196

194197
const handleFolderCreated = () => {
195198
setRefreshKey((prev) => prev + 1);
@@ -208,6 +211,11 @@ export default function FileManager() {
208211
setRefreshKey((prev) => prev + 1);
209212
};
210213

214+
const handlePreview = (file: FileItemData) => {
215+
setFileToPreview(file);
216+
setShowPreviewModal(true);
217+
};
218+
211219
const storageFiles: FileItemData[] = filterProfileItems(storages).map((storage) => ({
212220
id: storage.id,
213221
name: storage.name,
@@ -229,7 +237,7 @@ export default function FileManager() {
229237
const handleFileDoubleClick = (file: FileItemData) => {
230238
if (file.type === "folder") {
231239
const isStorage = storages.some(s => s.id === file.id);
232-
240+
233241
if (!selectedStorageId && isStorage) {
234242
setSelectedStorageId(file.id);
235243
setCurrentPath("/");
@@ -317,7 +325,7 @@ export default function FileManager() {
317325
</AuthWrapper>
318326
);
319327
}
320-
328+
321329
const isBrowsing = selectedStorageId && isLoadingFiles;
322330

323331
if (storagesError) {
@@ -390,6 +398,7 @@ export default function FileManager() {
390398
onFileSelect={handleFileSelect}
391399
onFileDoubleClick={handleFileDoubleClick}
392400
onFileRename={handleRename}
401+
onFilePreview={handlePreview}
393402
selectedFileIds={selectedFileIds}
394403
/>
395404
)}
@@ -424,6 +433,14 @@ export default function FileManager() {
424433
file={fileToRename}
425434
onRenamed={handleRenamed}
426435
/>
436+
<PreviewModal
437+
isOpen={showPreviewModal}
438+
onClose={() => {
439+
setShowPreviewModal(false);
440+
setFileToPreview(null);
441+
}}
442+
file={fileToPreview}
443+
/>
427444
<FileUploadHandler
428445
currentContainerUrl={containerUrlToBrowse}
429446
onUploadComplete={handleFileUploaded}

0 commit comments

Comments
 (0)