Skip to content

Commit 2dc8464

Browse files
authored
Merge pull request #27 from ethersphere/add_filtering
feat: create types filtering
2 parents 38010ee + b146bc5 commit 2dc8464

2 files changed

Lines changed: 306 additions & 8 deletions

File tree

src/app/components/UploadHistorySection.tsx

Lines changed: 194 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ interface UploadHistory {
1919
[address: string]: UploadRecord[];
2020
}
2121

22+
type FileType = 'all' | 'images' | 'videos' | 'audio' | 'archives' | 'websites';
23+
2224
const UploadHistorySection: React.FC<UploadHistoryProps> = ({ address, setShowUploadHistory }) => {
2325
const [history, setHistory] = React.useState<UploadRecord[]>([]);
26+
const [selectedFilter, setSelectedFilter] = React.useState<FileType>('all');
2427

2528
const formatStampId = (stampId: string) => {
2629
if (!stampId || typeof stampId !== 'string' || stampId.length < 10) {
@@ -61,6 +64,118 @@ const UploadHistorySection: React.FC<UploadHistoryProps> = ({ address, setShowUp
6164
return archiveExtensions.some(ext => filename.toLowerCase().endsWith(ext));
6265
};
6366

67+
// File type detection functions
68+
const getFileType = (filename?: string): FileType => {
69+
if (!filename) return 'all';
70+
71+
const extension = filename.toLowerCase();
72+
73+
// Image files
74+
const imageExtensions = [
75+
'.jpg',
76+
'.jpeg',
77+
'.png',
78+
'.gif',
79+
'.svg',
80+
'.webp',
81+
'.bmp',
82+
'.ico',
83+
'.tiff',
84+
'.tif',
85+
];
86+
if (imageExtensions.some(ext => extension.endsWith(ext))) {
87+
return 'images';
88+
}
89+
90+
// Video files
91+
const videoExtensions = [
92+
'.mp4',
93+
'.avi',
94+
'.mov',
95+
'.wmv',
96+
'.flv',
97+
'.webm',
98+
'.mkv',
99+
'.m4v',
100+
'.3gp',
101+
'.ogv',
102+
];
103+
if (videoExtensions.some(ext => extension.endsWith(ext))) {
104+
return 'videos';
105+
}
106+
107+
// Audio files
108+
const audioExtensions = ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma', '.m4a', '.opus'];
109+
if (audioExtensions.some(ext => extension.endsWith(ext))) {
110+
return 'audio';
111+
}
112+
113+
// Archive files
114+
const archiveExtensions = ['.zip', '.tar', '.gz', '.rar', '.7z', '.bz2'];
115+
if (archiveExtensions.some(ext => extension.endsWith(ext))) {
116+
return 'archives';
117+
}
118+
119+
// Website files (common web file extensions)
120+
const webExtensions = ['.html', '.htm', '.css', '.js', '.json'];
121+
if (webExtensions.some(ext => extension.endsWith(ext))) {
122+
return 'websites';
123+
}
124+
125+
return 'all';
126+
};
127+
128+
const getFileTypeLabel = (filename?: string): string => {
129+
const type = getFileType(filename);
130+
switch (type) {
131+
case 'images':
132+
return 'Image';
133+
case 'videos':
134+
return 'Video';
135+
case 'audio':
136+
return 'Audio';
137+
case 'archives':
138+
return 'Archive';
139+
case 'websites':
140+
return 'Website';
141+
default:
142+
return 'File';
143+
}
144+
};
145+
146+
// Filter history based on selected filter
147+
const filteredHistory = React.useMemo(() => {
148+
if (selectedFilter === 'all') {
149+
return history;
150+
}
151+
152+
return history.filter(record => {
153+
const fileType = getFileType(record.filename);
154+
return fileType === selectedFilter;
155+
});
156+
}, [history, selectedFilter]);
157+
158+
// Get filter counts
159+
const getFilterCounts = React.useMemo(() => {
160+
const counts = {
161+
all: history.length,
162+
images: 0,
163+
videos: 0,
164+
audio: 0,
165+
archives: 0,
166+
websites: 0,
167+
};
168+
169+
history.forEach(record => {
170+
const type = getFileType(record.filename);
171+
if (type !== 'all') {
172+
counts[type]++;
173+
}
174+
});
175+
176+
return counts;
177+
}, [history]);
178+
64179
const getReferenceUrl = (record: UploadRecord) => {
65180
// For non-archive files with a filename, include the filename in the URL
66181
if (record.filename && !isArchiveFile(record.filename)) {
@@ -71,25 +186,29 @@ const UploadHistorySection: React.FC<UploadHistoryProps> = ({ address, setShowUp
71186
};
72187

73188
const downloadCSV = () => {
74-
if (history.length === 0) return;
189+
// Use filtered history for CSV export
190+
const dataToExport = filteredHistory;
191+
if (dataToExport.length === 0) return;
75192

76-
// CSV headers
193+
// CSV headers - added File Type column
77194
const headers = [
78195
'Reference',
79196
'Stamp ID',
80197
'Date Created',
81198
'Expiry (Days)',
82199
'Filename',
200+
'File Type',
83201
'Full Link',
84202
];
85203

86204
// Convert history data to CSV rows
87-
const csvRows = history.map(record => [
205+
const csvRows = dataToExport.map(record => [
88206
record.reference,
89207
record.stampId,
90208
formatDate(record.timestamp),
91209
formatExpiryDays(record.expiryDate),
92210
record.filename || 'Unnamed upload',
211+
getFileTypeLabel(record.filename),
93212
getReferenceUrl(record),
94213
]);
95214

@@ -103,9 +222,12 @@ const UploadHistorySection: React.FC<UploadHistoryProps> = ({ address, setShowUp
103222
const link = document.createElement('a');
104223
const url = URL.createObjectURL(blob);
105224
link.setAttribute('href', url);
225+
226+
// Include filter in filename if not 'all'
227+
const filterSuffix = selectedFilter !== 'all' ? `-${selectedFilter}` : '';
106228
link.setAttribute(
107229
'download',
108-
`upload-history-${address?.slice(0, 8)}-${new Date().toISOString().split('T')[0]}.csv`
230+
`upload-history-${address?.slice(0, 8)}${filterSuffix}-${new Date().toISOString().split('T')[0]}.csv`
109231
);
110232
link.style.visibility = 'hidden';
111233
document.body.appendChild(link);
@@ -134,7 +256,15 @@ const UploadHistorySection: React.FC<UploadHistoryProps> = ({ address, setShowUp
134256
const fields = line.split(',').map(field => field.replace(/^"|"$/g, '').trim());
135257

136258
if (fields.length >= 6) {
137-
const [reference, stampId, dateCreated, expiryDays, filename, fullLink] = fields;
259+
const [
260+
reference,
261+
stampId,
262+
dateCreated,
263+
expiryDays,
264+
filename,
265+
fileTypeOrFullLink,
266+
fullLink,
267+
] = fields;
138268

139269
// Skip if reference already exists (prevent duplicates)
140270
if (existingReferences.has(reference)) {
@@ -213,7 +343,7 @@ const UploadHistorySection: React.FC<UploadHistoryProps> = ({ address, setShowUp
213343
<div className={styles.titleContainer}>
214344
<h2 className={styles.title}>Upload History</h2>
215345
<div className={styles.buttonGroup}>
216-
{history.length > 0 && (
346+
{filteredHistory.length > 0 && (
217347
<button className={styles.downloadButton} onClick={downloadCSV} title="Download CSV">
218348
<svg
219349
width="20"
@@ -277,16 +407,72 @@ const UploadHistorySection: React.FC<UploadHistoryProps> = ({ address, setShowUp
277407
</div>
278408
</div>
279409

410+
{/* Filter buttons */}
411+
{history.length > 0 && (
412+
<div className={styles.filterContainer}>
413+
<div className={styles.filterButtons}>
414+
<button
415+
className={`${styles.filterButton} ${selectedFilter === 'all' ? styles.activeFilter : ''}`}
416+
onClick={() => setSelectedFilter('all')}
417+
>
418+
All ({getFilterCounts.all})
419+
</button>
420+
<button
421+
className={`${styles.filterButton} ${selectedFilter === 'images' ? styles.activeFilter : ''}`}
422+
onClick={() => setSelectedFilter('images')}
423+
disabled={getFilterCounts.images === 0}
424+
>
425+
Images ({getFilterCounts.images})
426+
</button>
427+
<button
428+
className={`${styles.filterButton} ${selectedFilter === 'videos' ? styles.activeFilter : ''}`}
429+
onClick={() => setSelectedFilter('videos')}
430+
disabled={getFilterCounts.videos === 0}
431+
>
432+
Videos ({getFilterCounts.videos})
433+
</button>
434+
<button
435+
className={`${styles.filterButton} ${selectedFilter === 'audio' ? styles.activeFilter : ''}`}
436+
onClick={() => setSelectedFilter('audio')}
437+
disabled={getFilterCounts.audio === 0}
438+
>
439+
Audio ({getFilterCounts.audio})
440+
</button>
441+
<button
442+
className={`${styles.filterButton} ${selectedFilter === 'archives' ? styles.activeFilter : ''}`}
443+
onClick={() => setSelectedFilter('archives')}
444+
disabled={getFilterCounts.archives === 0}
445+
>
446+
Archives ({getFilterCounts.archives})
447+
</button>
448+
<button
449+
className={`${styles.filterButton} ${selectedFilter === 'websites' ? styles.activeFilter : ''}`}
450+
onClick={() => setSelectedFilter('websites')}
451+
disabled={getFilterCounts.websites === 0}
452+
>
453+
Websites ({getFilterCounts.websites})
454+
</button>
455+
</div>
456+
</div>
457+
)}
458+
280459
{!address ? (
281460
<div className={styles.emptyState}>Connect wallet to check upload history</div>
282461
) : history.length === 0 ? (
283462
<div className={styles.emptyState}>No uploads found for this address</div>
463+
) : filteredHistory.length === 0 ? (
464+
<div className={styles.emptyState}>
465+
No {selectedFilter === 'all' ? 'files' : selectedFilter} found in your upload history
466+
</div>
284467
) : (
285468
<div className={styles.historyList}>
286-
{history.map((record, index) => (
469+
{filteredHistory.map((record, index) => (
287470
<div key={index} className={styles.historyItem}>
288471
<div className={styles.itemHeader}>
289-
<span className={styles.filename}>{record.filename || 'Unnamed upload'}</span>
472+
<div className={styles.filenameContainer}>
473+
<span className={styles.filename}>{record.filename || 'Unnamed upload'}</span>
474+
<span className={styles.fileType}>{getFileTypeLabel(record.filename)}</span>
475+
</div>
290476
<span className={styles.date}>{formatDate(record.timestamp)}</span>
291477
</div>
292478
<div className={styles.itemDetails}>

0 commit comments

Comments
 (0)