From dff95c414b9a9d6a3d7c323572e6d38f0e43854b Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Fri, 1 May 2026 23:17:40 +0000 Subject: [PATCH] frontend: trim document formats, simplify type getters, use FILE_TYPES consistently - Trim document extension list to common office formats only (doc/docx/xls/xlsx/ppt/pptx/odt/ods/odp/rtf), removing txt/md/csv/html/xml/etc. - Simplify Doc getters: merge guard clauses into single returns (img, previewable, previewurl, ext). - Simplify MediaPreview.preview() into single boolean expression. - Use global FILE_TYPES instead of inline extension lists. --- frontend/src/components/MediaPreview.vue | 80 ++------------- frontend/src/repositories/Document.ts | 123 +++++++---------------- frontend/src/utils/index.ts | 30 +++--- 3 files changed, 66 insertions(+), 167 deletions(-) diff --git a/frontend/src/components/MediaPreview.vue b/frontend/src/components/MediaPreview.vue index da3bd22..4cdcfc7 100644 --- a/frontend/src/components/MediaPreview.vue +++ b/frontend/src/components/MediaPreview.vue @@ -126,80 +126,20 @@ defineExpose({ media }) -const video = () => ['mkv', 'mp4', 'webm', 'mov', 'avi'].includes(props.doc.ext) -const audio = () => ['mp3', 'flac', 'ogg', 'aac'].includes(props.doc.ext) -const archive = () => - ['zip', 'tar', 'gz', 'bz2', 'xz', '7z', 'rar'].includes(props.doc.ext) +const video = () => props.doc.video +const audio = () => props.doc.audio +const archive = () => props.doc.archive +const docs = () => props.doc.document +// image = requires server-side preview (browsers cannot display it natively) +// img = browser-viewable image that can be used directly in an tag +const image = () => props.doc.image +const print = () => props.doc.print const showProgress = () => !props.doc.complete && (preview() || props.doc.img) const preview = () => { const store = useMainStore() - const ext = props.doc.ext - // Office document previews may be optionally disabled server-side - if (store.server.office_previews === false) { - const officeExts = [ - 'doc', 'dot', 'docx', 'docm', 'dotx', 'dotm', 'rtf', - 'odt', 'ott', 'txt', 'md', 'mhtml', 'mht', 'html', - 'htm', 'xml', 'wps', 'wri', - 'xls', 'xlsx', 'xlsm', 'xlsb', 'xltx', 'xltm', - 'ods', 'ots', 'csv', - 'ppt', 'pptx', 'pptm', 'pps', 'ppsx', - 'pot', 'potx', 'odp', 'otp' - ] - if (officeExts.includes(ext)) return false - } return ( - [ - 'bmp', - 'ico', - 'tif', - 'tiff', - 'heic', - 'heif', - 'pdf', - 'epub', - 'mobi', - // Documents - 'doc', - 'dot', - 'docx', - 'docm', - 'dotx', - 'dotm', - 'rtf', - 'odt', - 'ott', - 'txt', - 'md', - 'mhtml', - 'mht', - 'html', - 'htm', - 'xml', - 'wps', - 'wri', - // Spreadsheets - 'xls', - 'xlsx', - 'xlsm', - 'xlsb', - 'xltx', - 'xltm', - 'ods', - 'ots', - 'csv', - // Presentations - 'ppt', - 'pptx', - 'pptm', - 'pps', - 'ppsx', - 'pot', - 'potx', - 'odp', - 'otp' - ].includes(ext) || - (props.doc.size > 500000 && - ['avif', 'webp', 'png', 'jpg', 'jpeg'].includes(ext)) + !(store.server.office_previews === false && docs()) && + (image() || print() || (props.doc.img && props.doc.size > 500000)) ) } diff --git a/frontend/src/repositories/Document.ts b/frontend/src/repositories/Document.ts index 6668e9b..8aa8e56 100644 --- a/frontend/src/repositories/Document.ts +++ b/frontend/src/repositories/Document.ts @@ -1,4 +1,5 @@ -import { formatSize, formatUnixDate } from '@/utils' +import { useMainStore } from '@/stores/main' +import { FILE_TYPES, formatSize, formatUnixDate } from '@/utils' export type FUID = string @@ -63,101 +64,53 @@ export class Doc { return this.url.replace(/^\/#/, '') } get img(): boolean { - // Folders cannot be images - if (this.dir) return false - return [ - 'jpg', - 'jpeg', - 'png', - 'gif', - 'webp', - 'avif', - 'heic', - 'heif', - 'svg' - ].includes(this.ext) + return ( + !this.dir && (FILE_TYPES.imageBrowser as readonly string[]).includes(this.ext) + ) + } + get video(): boolean { + return (FILE_TYPES.video as readonly string[]).includes(this.ext) + } + get audio(): boolean { + return (FILE_TYPES.audio as readonly string[]).includes(this.ext) + } + get archive(): boolean { + return (FILE_TYPES.archive as readonly string[]).includes(this.ext) + } + get document(): boolean { + return (FILE_TYPES.document as readonly string[]).includes(this.ext) + } + // Images that require server-side preview (browsers cannot display them natively) + get image(): boolean { + return (FILE_TYPES.image as readonly string[]).includes(this.ext) + } + get print(): boolean { + return (FILE_TYPES.print as readonly string[]).includes(this.ext) } get complete(): boolean { return !this.ghost && (this.dir || this.size <= this.allocated) } get previewable(): boolean { - // Folders cannot be previewable if (this.dir) return false - if (this.img) return true - const store = useMainStore() - const ext = this.ext - // Office document previews may be optionally disabled server-side - if (store.server.office_previews === false) { - const officeExts = [ - 'doc', 'dot', 'docx', 'docm', 'dotx', 'dotm', 'rtf', - 'odt', 'ott', 'txt', 'md', 'mhtml', 'mht', 'html', - 'htm', 'xml', 'wps', 'wri', - 'xls', 'xlsx', 'xlsm', 'xlsb', 'xltx', 'xltm', - 'ods', 'ots', 'csv', - 'ppt', 'pptx', 'pptm', 'pps', 'ppsx', - 'pot', 'potx', 'odp', 'otp' - ] - if (officeExts.includes(ext)) return false - } - // Not a comprehensive list, but good enough for now - return [ - 'mp4', - 'mkv', - 'webm', - 'ogg', - 'mp3', - 'flac', - 'aac', - 'pdf', - // Documents - 'doc', - 'dot', - 'docx', - 'docm', - 'dotx', - 'dotm', - 'rtf', - 'odt', - 'ott', - 'txt', - 'md', - 'mhtml', - 'mht', - 'html', - 'htm', - 'xml', - 'wps', - 'wri', - // Spreadsheets - 'xls', - 'xlsx', - 'xlsm', - 'xlsb', - 'xltx', - 'xltm', - 'ods', - 'ots', - 'csv', - // Presentations - 'ppt', - 'pptx', - 'pptm', - 'pps', - 'ppsx', - 'pot', - 'potx', - 'odp', - 'otp' - ].includes(ext) + return ( + this.img || + this.video || + this.audio || + this.image || + this.print || + (this.document && useMainStore().server.office_previews !== false) + ) } get previewurl(): string { - if (!this.complete || !this.previewable) return '' - return this.url.replace(/^\/files/, '/preview') + return !this.complete || !this.previewable + ? '' + : this.url.replace(/^\/files/, '/preview') } get ext(): string { const dotIndex = this.name.lastIndexOf('.') - if (dotIndex === -1 || dotIndex === this.name.length - 1) return '' - return this.name.slice(dotIndex + 1).toLowerCase() + return dotIndex === -1 || dotIndex === this.name.length - 1 + ? '' + : this.name.slice(dotIndex + 1).toLowerCase() } } export type errorEvent = { diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index e862dd7..61f345a 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -69,23 +69,29 @@ export function getFileExtension(filename: string) { } return filename.slice(dotIndex + 1) } -interface FileTypes { - [key: string]: string[] -} - -const filetypes: FileTypes = { +export const FILE_TYPES = { video: ['avi', 'mkv', 'mov', 'mp4', 'webm'], - image: ['avif', 'gif', 'jpg', 'jpeg', 'png', 'webp', 'svg'], - pdf: ['pdf'] -} + audio: ['mp3', 'flac', 'ogg', 'aac'], + archive: ['zip', 'tar', 'gz', 'bz2', 'xz', '7z', 'rar'], + document: ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'odt', 'ods', 'odp', 'rtf'], + imageBrowser: ['avif', 'gif', 'jpg', 'jpeg', 'png', 'webp', 'svg'], + // Images that require server-side preview (browsers cannot display them natively) + image: ['bmp', 'heic', 'heif', 'ico', 'tif', 'tiff'], + print: ['epub', 'mobi', 'pdf'] +} as const -export function getFileType(name: string): string { +export type FileCategory = keyof typeof FILE_TYPES + +export function getFileType(name: string): FileCategory | 'unknown' { const dotIndex = name.lastIndexOf('.') if (dotIndex === -1 || dotIndex === name.length - 1) return 'unknown' const ext = name.slice(dotIndex + 1).toLowerCase() - return ( - Object.keys(filetypes).find(type => filetypes[type]!.includes(ext)) || 'unknown' - ) + for (const category of Object.keys(FILE_TYPES) as FileCategory[]) { + if ((FILE_TYPES[category] as readonly string[]).includes(ext)) { + return category + } + } + return 'unknown' } // Prebuilt for fast & consistent sorting