191 lines
6.2 KiB
JavaScript
Raw Normal View History

2025-11-09 23:29:07 +01:00
import { api } from '../api.js';
class FilePreview extends HTMLElement {
constructor() {
super();
this.file = null;
this.handleEscape = this.handleEscape.bind(this);
}
connectedCallback() {
this.render();
this.setupEventListeners();
}
setupEventListeners() {
const closeBtn = this.querySelector('.close-preview');
const downloadBtn = this.querySelector('.download-btn');
const shareBtn = this.querySelector('.share-btn');
2025-11-10 15:46:40 +01:00
if (closeBtn) {
closeBtn.addEventListener('click', () => this.close());
}
if (downloadBtn) {
downloadBtn.addEventListener('click', () => this.downloadFile());
}
2025-11-09 23:29:07 +01:00
2025-11-10 15:46:40 +01:00
if (shareBtn) {
shareBtn.addEventListener('click', () => this.shareFile());
}
2025-11-09 23:29:07 +01:00
}
handleEscape(e) {
if (e.key === 'Escape' && this.style.display === 'block') {
this.close();
}
}
2025-11-10 15:46:40 +01:00
async show(file, pushState = true) {
2025-11-09 23:29:07 +01:00
this.file = file;
this.style.display = 'block';
document.addEventListener('keydown', this.handleEscape);
this.renderPreview();
2025-11-10 15:46:40 +01:00
if (pushState) {
window.history.pushState(
{ view: 'file-preview', file: file },
'',
`#preview/${file.id}`
);
}
2025-11-09 23:29:07 +01:00
}
close() {
2025-11-10 15:46:40 +01:00
window.history.back();
}
hide() {
2025-11-09 23:29:07 +01:00
this.style.display = 'none';
this.file = null;
document.removeEventListener('keydown', this.handleEscape);
2025-11-10 15:46:40 +01:00
this.remove();
2025-11-09 23:29:07 +01:00
}
async renderPreview() {
const previewContent = this.querySelector('.preview-content');
const fileName = this.querySelector('.preview-file-name');
const fileInfo = this.querySelector('.preview-file-info');
fileName.textContent = this.file.name;
fileInfo.textContent = `${this.formatFileSize(this.file.size)} • ${new Date(this.file.created_at).toLocaleDateString()}`;
if (this.file.mime_type.startsWith('image/')) {
previewContent.innerHTML = `<img alt="${this.file.name}">`;
this.loadAuthenticatedMedia(previewContent.querySelector('img'));
} else if (this.file.mime_type.startsWith('video/')) {
previewContent.innerHTML = `
<video controls>
Your browser does not support the video tag.
</video>
`;
this.loadAuthenticatedMedia(previewContent.querySelector('video'));
} else if (this.file.mime_type.startsWith('audio/')) {
previewContent.innerHTML = `
<audio controls>
Your browser does not support the audio tag.
</audio>
`;
this.loadAuthenticatedMedia(previewContent.querySelector('audio'));
} else if (this.file.mime_type === 'application/pdf') {
previewContent.innerHTML = `<iframe type="application/pdf"></iframe>`;
this.loadAuthenticatedMedia(previewContent.querySelector('iframe'));
} else if (this.file.mime_type.startsWith('text/')) {
this.loadTextPreview(previewContent);
} else {
previewContent.innerHTML = `
<div class="no-preview">
<p>Preview not available for this file type</p>
<button class="btn btn-primary" onclick="this.getRootNode().host.downloadFile()">Download File</button>
</div>
`;
}
}
async loadAuthenticatedMedia(element) {
try {
const blob = await api.downloadFile(this.file.id);
const url = URL.createObjectURL(blob);
element.src = url;
} catch (error) {
console.error('Failed to load media:', error);
element.parentElement.innerHTML = '<p>Failed to load preview</p>';
}
}
async loadTextPreview(container) {
try {
const blob = await api.downloadFile(this.file.id);
const text = await blob.text();
container.innerHTML = `<pre>${this.escapeHtml(text)}</pre>`;
} catch (error) {
container.innerHTML = '<p>Failed to load preview</p>';
}
}
async downloadFile() {
try {
const blob = await api.downloadFile(this.file.id);
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = this.file.name;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('Failed to download file:', error);
}
}
shareFile() {
this.dispatchEvent(new CustomEvent('share-file', {
detail: { file: this.file },
bubbles: true
}));
}
formatFileSize(bytes) {
const units = ['B', 'KB', 'MB', 'GB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(1)} ${units[unitIndex]}`;
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
render() {
this.innerHTML = `
2025-11-10 15:46:40 +01:00
<div class="file-preview-overlay">
<div class="file-preview-header">
<div class="header-left">
<button class="button close-preview">Back</button>
2025-11-09 23:29:07 +01:00
<div class="preview-info">
2025-11-10 15:46:40 +01:00
<h2 class="preview-file-name"></h2>
2025-11-09 23:29:07 +01:00
<p class="preview-file-info"></p>
</div>
</div>
2025-11-10 15:46:40 +01:00
<div class="preview-actions">
<button class="button download-btn">Download</button>
<button class="button share-btn">Share</button>
</div>
2025-11-09 23:29:07 +01:00
</div>
2025-11-10 15:46:40 +01:00
<div class="preview-content"></div>
2025-11-09 23:29:07 +01:00
</div>
`;
this.style.display = 'none';
}
}
customElements.define('file-preview', FilePreview);
export { FilePreview };