diff --git a/cshare-app/src/pages/FileViewPage.jsx b/cshare-app/src/pages/FileViewPage.jsx
new file mode 100644
index 0000000..5925b01
--- /dev/null
+++ b/cshare-app/src/pages/FileViewPage.jsx
@@ -0,0 +1,576 @@
+import { useState, useEffect } from 'react';
+import { useParams } from 'react-router-dom';
+import { DownloadIcon, CopyIcon, CheckIcon, ClockIcon, ShieldIcon } from '../components/Icons';
+import { sharedFile, formatFileSize, formatDate, getFileIcon, getFileIconColor } from '../data/mockData';
+import { useToast } from '../components/ToastContext';
+import { downloadUrl, previewUrl, resolveLink } from '../lib/api';
+
+function useCountdown(targetDate) {
+ const [timeLeft, setTimeLeft] = useState('');
+
+ useEffect(() => {
+ const update = () => {
+ const diff = new Date(targetDate) - new Date();
+ if (diff <= 0) { setTimeLeft('Expired'); return; }
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24));
+ const hours = Math.floor((diff / (1000 * 60 * 60)) % 24);
+ const mins = Math.floor((diff / (1000 * 60)) % 60);
+ const secs = Math.floor((diff / 1000) % 60);
+ if (days > 0) {
+ setTimeLeft(`${days}d ${hours}h ${mins}m ${secs}s`);
+ } else {
+ setTimeLeft(`${hours}h ${mins}m ${secs}s`);
+ }
+ };
+ update();
+ const id = setInterval(update, 1000);
+ return () => clearInterval(id);
+ }, [targetDate]);
+
+ return timeLeft;
+}
+
+function FilePreview({ file, linkId }) {
+ const [text, setText] = useState('');
+ const [textError, setTextError] = useState('');
+ const [zoom, setZoom] = useState(1);
+ const [rotation, setRotation] = useState(0);
+ const [copiedText, setCopiedText] = useState(false);
+ const [searchTerm, setSearchTerm] = useState('');
+ const addToast = useToast();
+ const preview = file.preview;
+
+ useEffect(() => {
+ if (!linkId || preview?.kind !== 'text') return;
+ let active = true;
+ fetch(previewUrl(linkId))
+ .then((response) => {
+ if (!response.ok) throw new Error('Preview unavailable');
+ return response.text();
+ })
+ .then((body) => {
+ if (active) setText(body.slice(0, 120_000));
+ })
+ .catch((err) => {
+ if (active) setTextError(err.message);
+ });
+ return () => {
+ active = false;
+ };
+ }, [linkId, preview?.kind]);
+
+ const copyTextContent = () => {
+ navigator.clipboard?.writeText(text);
+ setCopiedText(true);
+ addToast('Preview text copied to clipboard', 'success');
+ setTimeout(() => setCopiedText(false), 2000);
+ };
+
+ if (!linkId || !preview?.available) {
+ return (
+
+ Preview is available for small images, PDFs, text, audio, and video files.
+
+ );
+ }
+
+ const src = previewUrl(linkId);
+ const shellStyle = {
+ marginBottom: '20px',
+ background: 'var(--bg-surface)',
+ border: '1px solid var(--border-subtle)',
+ borderRadius: 'var(--radius-md)',
+ overflow: 'hidden',
+ };
+
+ if (preview.kind === 'image') {
+ return (
+
+ {/* Toolbar */}
+
+
+
+
+
+
+ {/* Canvas container */}
+
+

+ {/* Zoom badge */}
+
+ {Math.round(zoom * 100)}%
+
+
+
+ );
+ }
+
+ if (preview.kind === 'video') {
+ return (
+
+
+
+ );
+ }
+
+ if (preview.kind === 'audio') {
+ return (
+
+ );
+ }
+
+ if (preview.kind === 'pdf') {
+ return (
+
+
+
+ );
+ }
+
+ if (preview.kind === 'text') {
+ const lines = text.split('\n');
+ const filteredLines = searchTerm
+ ? lines.map((line, idx) => ({ line, idx })).filter(item => item.line.toLowerCase().includes(searchTerm.toLowerCase()))
+ : lines.map((line, idx) => ({ line, idx }));
+
+ return (
+
+ {/* Toolbar */}
+
+ setSearchTerm(e.target.value)}
+ style={{
+ padding: '4px 10px',
+ fontSize: '0.75rem',
+ maxWidth: '180px',
+ height: '28px',
+ }}
+ />
+
+
+ {/* Text Area */}
+
+ {textError ? (
+
{textError}
+ ) : !text ? (
+
Loading preview...
+ ) : (
+
+ {/* Line Numbers gutter */}
+
+ {filteredLines.map((item) => (
+
{item.idx + 1}
+ ))}
+
+ {/* Lines Content */}
+
+ {filteredLines.map((item) => (
+
+ {item.line || ' '}
+
+ ))}
+
+
+ )}
+
+
+ );
+ }
+
+ return null;
+}
+
+export default function FileViewPage() {
+ const { linkId } = useParams();
+ const [file, setFile] = useState(sharedFile);
+ const [error, setError] = useState('');
+ const [copied, setCopied] = useState(false);
+ const [downloading, setDownloading] = useState(false);
+ const [downloadProgress, setDownloadProgress] = useState(0);
+ const addToast = useToast();
+ const countdown = useCountdown(file.expiresAt);
+
+ useEffect(() => {
+ if (!linkId) return;
+ let active = true;
+ resolveLink(linkId)
+ .then((remoteFile) => {
+ if (!active) return;
+ setFile(remoteFile);
+ setError('');
+ })
+ .catch((err) => {
+ if (!active) return;
+ setError(err.message);
+ });
+ return () => {
+ active = false;
+ };
+ }, [linkId]);
+
+ const copyLink = () => {
+ navigator.clipboard?.writeText(file.link);
+ setCopied(true);
+ addToast('Link copied to clipboard', 'success');
+ setTimeout(() => setCopied(false), 2000);
+ };
+
+ const handleDownload = () => {
+ if (linkId) {
+ setDownloading(true);
+ setDownloadProgress(100);
+ window.location.href = downloadUrl(linkId);
+ setTimeout(() => {
+ setDownloading(false);
+ setDownloadProgress(0);
+ }, 1200);
+ return;
+ }
+
+ setDownloading(true);
+ setDownloadProgress(0);
+
+ let progress = 0;
+ const interval = setInterval(() => {
+ progress += Math.random() * 20 + 10;
+ if (progress >= 100) {
+ progress = 100;
+ clearInterval(interval);
+ setDownloadProgress(100);
+ setTimeout(() => {
+ setDownloading(false);
+ setDownloadProgress(0);
+ addToast('Download complete — this is a demo', 'success');
+ }, 400);
+ } else {
+ setDownloadProgress(Math.min(progress, 99));
+ }
+ }, 200);
+ };
+
+ return (
+
+ {/* Animated background orbs */}
+
+
+
+
+ {/* Header badge */}
+
+
+
+
+
+ Shared with you
+
+
+ {error || 'Someone shared this file via CShare. Download it before the link expires.'}
+
+
+
+ {/* File card with glow */}
+
+ {/* Accent bar with glow */}
+
+
+ {/* File icon + name */}
+
+
+ {getFileIcon(file.type)}
+
+
+
+
+ {file.name}
+
+
+ {formatFileSize(file.size)}
+
+
+
+
+ {/* Metadata grid */}
+
+
+
+ Uploaded by
+
+
+ {file.uploader}
+
+
+
+
+ Uploaded on
+
+
+ {formatDate(file.uploadedAt)}
+
+
+
+
+ Expires
+
+
+
+ {countdown}
+
+
+
+
+ Views
+
+
+ {file.views}
+
+
+
+
+
+
+ {/* Actions */}
+
+
+
+ {/* Download progress bar */}
+ {downloading && (
+
+ )}
+
+
+
+
+
+ {/* Trust footer */}
+
+
+
+ End-to-end encrypted and auto-expiring
+
+
+
+
+ );
+}