diff --git a/control/frontend/src/components/FileView.svelte b/control/frontend/src/components/FileView.svelte index b422d44..9101d1a 100644 --- a/control/frontend/src/components/FileView.svelte +++ b/control/frontend/src/components/FileView.svelte @@ -20,8 +20,62 @@ import { slide } from 'svelte/transition'; import FolderElementObject from './FolderElementObject.svelte'; import PopUp from './PopUp.svelte'; + import type { PopupContent } from '../ts/types'; + import TextInput from './TextInput.svelte'; + import { is_valid_name } from '../ts/utils'; + + let current_name: string = ''; + let current_valid: boolean = false; + + let popup_content: PopupContent = $state({ + open: false, + snippet: null, + title: '', + closable: true + }); + + function popup_close_function() { + popup_content.open = false; + } + + const show_new_folder_popup = () => { + popup_content = { + open: true, + snippet: new_folder_popup, + title: 'Neuen Ordner erstellen', + title_icon: FolderPlus, + closable: true + }; + }; +{#snippet new_folder_popup()} +
+ { + if (input.startsWith('.')) return [false, 'Name darf nicht mit . beginnen']; + const trimmed_input = input.trim(); + if (trimmed_input.length === 0 || trimmed_input.length > 50) + return [false, 'Ungültige Länge']; + if (!is_valid_name(trimmed_input)) return [false, 'Name enthält ungültige Zeichen']; + if ( + get_current_folder_elements($all_files, $current_file_path, $selected_display_ids).some( + (e) => e.name === trimmed_input + ) + ) + return [false, 'Name bereits verwendet']; + return [true, 'Gültiger Name']; + }} + /> +
+
+ +
+{/snippet} +
@@ -59,7 +113,8 @@
@@ -125,8 +180,11 @@ {/if}
- +
diff --git a/control/frontend/src/components/FolderElementObject.svelte b/control/frontend/src/components/FolderElementObject.svelte index c499c0e..75d9442 100644 --- a/control/frontend/src/components/FolderElementObject.svelte +++ b/control/frontend/src/components/FolderElementObject.svelte @@ -14,7 +14,8 @@ get_shifted_color } from '../ts/stores/ui_behavior'; import Button from './Button.svelte'; - import { supported_file_types, type FolderElement, type SupportedFileType } from '../ts/types'; + import { supported_file_type_icon, type FolderElement, type SupportedFileType } from '../ts/types'; + import { is_selected, select, @@ -28,7 +29,7 @@ get_display_ids_where_file_is_missing } from '../ts/stores/files'; import RefreshPlay from './RefreshPlay.svelte'; - import { get_file_size_display_string } from '../ts/utils'; + import { get_file_size_display_string, get_file_type } from '../ts/utils'; import { open_file } from '../ts/api_handler'; import { displays, @@ -55,22 +56,6 @@ const is_folder = file.type === 'inode/directory'; - function get_file_type(file: FolderElement): SupportedFileType | null { - for (const key of Object.keys(supported_file_types)) { - if (file.type === supported_file_types[key].mime_type) { - return supported_file_types[key]; - } - } - // Fallback: - const extension = file.name.split('.').pop(); - if (extension) { - if (Object.keys(supported_file_types).includes('.' + extension)) { - return supported_file_types['.' + extension]; - } - } - return null; - } - function get_created_string(date_object: Date, full_string = false) { if (full_string) { return ( @@ -191,11 +176,11 @@ file_thumbnail - {:else if get_file_type(file)?.icon} - {@const Icon = get_file_type(file)?.icon} + {:else if supported_file_type_icon[get_file_type(file)?.display_name || '']} + {@const Icon = supported_file_type_icon[get_file_type(file)?.display_name || '']} {:else} diff --git a/control/frontend/src/components/TextInput.svelte b/control/frontend/src/components/TextInput.svelte index a4a4575..118abb3 100644 --- a/control/frontend/src/components/TextInput.svelte +++ b/control/frontend/src/components/TextInput.svelte @@ -49,7 +49,10 @@ {title}: {#if is_valid_function && focussed} -
+
{current_info}
{/if} diff --git a/control/frontend/src/ts/stores/thumbnails.ts b/control/frontend/src/ts/stores/thumbnails.ts index 064d8e1..04c6a15 100644 --- a/control/frontend/src/ts/stores/thumbnails.ts +++ b/control/frontend/src/ts/stores/thumbnails.ts @@ -1,12 +1,14 @@ import { get, writable, type Writable } from "svelte/store"; import { get_thumbnail_blob } from "../api_handler"; -import { supported_file_types, type FolderElement } from "../types"; +import { type FolderElement } from "../types"; import { db } from "../indexdb/file_thumbnails.db"; +import { get_file_type } from "../utils"; export const active_thumbnail_urls: Writable = writable([]); export async function generate_thumbnail(display_ip: string, path: string, folder_element: FolderElement): Promise { - if (!Object.values(supported_file_types).some(e => e.mime_type === folder_element.type) || !folder_element.hash) return; + const supported_file_type = get_file_type(folder_element); + if (!supported_file_type || !folder_element.hash) return; const hash: string = folder_element.hash; if (await db.thumbnail_blobs.get(hash)) return; diff --git a/control/frontend/src/ts/types.ts b/control/frontend/src/ts/types.ts index 5a34cab..d57f325 100644 --- a/control/frontend/src/ts/types.ts +++ b/control/frontend/src/ts/types.ts @@ -1,48 +1,18 @@ -import { FileBox, FileImage, FileVideoCamera, ImagePlay, type X } from "lucide-svelte"; +import { FileBox, FileImage, FileText, FileVideoCamera, ImagePlay, type X } from "lucide-svelte"; import type { Snippet } from "svelte"; export type SupportedFileType = { - display_name: - string; mime_type: - string; icon: typeof X; + display_name: string; + mime_type: string; }; - -export const supported_file_types: Record = { - '.mp4': { - display_name: 'MP4', - mime_type: 'video/mp4', - icon: FileVideoCamera, - }, - '.jpg': { - display_name: 'JPG', - mime_type: 'image/jpg', - icon: FileImage, - }, - '.jpeg': { - display_name: 'JPG', - mime_type: 'image/jpeg', - icon: FileImage, - }, - '.png': { - display_name: 'PNG', - mime_type: 'image/png', - icon: FileImage, - }, - '.gif': { - display_name: 'GIF', - mime_type: 'image/gif', - icon: ImagePlay, - }, - '.pptx': { - display_name: 'PPTX', - mime_type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - icon: FileBox, - }, - '.odp': { - display_name: 'ODP', - mime_type: 'application/vnd.oasis.opendocument.presentation', - icon: FileBox, - } +export const supported_file_type_icon: Record = { + 'MP4': FileVideoCamera, + 'JPG': FileImage, + 'PNG': FileImage, + 'GIF': ImagePlay, + 'PPTX': FileBox, + 'ODP': FileBox, + 'PDF': FileText } export type FolderElement = { diff --git a/control/frontend/src/ts/utils.ts b/control/frontend/src/ts/utils.ts index 216f0d7..e0d7c28 100644 --- a/control/frontend/src/ts/utils.ts +++ b/control/frontend/src/ts/utils.ts @@ -1,19 +1,40 @@ -export function get_uuid(): string { - return crypto.randomUUID(); +import type { FolderElement, SupportedFileType } from "./types"; +import supported_file_types_json from './../../../../shared/supported_file_types.json'; + +const supported_file_types: Record = supported_file_types_json as Record; + +export function get_file_type(file: FolderElement): SupportedFileType | null { + for (const key of Object.keys(supported_file_types)) { + if (file.type === supported_file_types[key].mime_type) { + return supported_file_types[key]; + } + } + // Fallback: + const extension = file.name.split('.').pop(); + if (extension) { + if (Object.keys(supported_file_types).includes('.' + extension)) { + return supported_file_types['.' + extension]; + } + } + return null; } -export function get_file_size_display_string(size: number, toFixed: number|null = null): string { - if (size === 0) return "0 B"; +export function get_uuid(): string { + return crypto.randomUUID(); +} - const k = 1024; - const sizes = ["B", "KB", "MB", "GB", "TB"]; +export function get_file_size_display_string(size: number, toFixed: number | null = null): string { + if (size === 0) return "0 B"; - const i = Math.floor(Math.log(size) / Math.log(k)); - const value = size / Math.pow(k, i); + const k = 1024; + const sizes = ["B", "KB", "MB", "GB", "TB"]; - const size_string = `${value.toFixed(toFixed !== null ? toFixed : Math.max(0, 2 - Math.floor(Math.log10(value))))} ${sizes[i]}`; + const i = Math.floor(Math.log(size) / Math.log(k)); + const value = size / Math.pow(k, i); - return size_string.replace('.', ','); + const size_string = `${value.toFixed(toFixed !== null ? toFixed : Math.max(0, 2 - Math.floor(Math.log10(value))))} ${sizes[i]}`; + + return size_string.replace('.', ','); } @@ -40,3 +61,9 @@ export async function image_content_hash(blob: Blob, size = 32): Promise return hash >>> 0; // unsigned int } + + +export function is_valid_name(input: string): boolean { + return /^[\p{L}\p{N}\p{M}\-_.+,()[\]{}@!§$%&=~^ ]+$/u.test(input); +} + diff --git a/shared/supported_file_types.json b/shared/supported_file_types.json new file mode 100644 index 0000000..df79c21 --- /dev/null +++ b/shared/supported_file_types.json @@ -0,0 +1,34 @@ +{ + ".mp4": { + "display_name": "MP4", + "mime_type": "video/mp4" + }, + ".jpg": { + "display_name": "JPG", + "mime_type": "image/jpg" + }, + ".jpeg": { + "display_name": "JPG", + "mime_type": "image/jpeg" + }, + ".png": { + "display_name": "PNG", + "mime_type": "image/png" + }, + ".gif": { + "display_name": "GIF", + "mime_type": "image/gif" + }, + ".pptx": { + "display_name": "PPTX", + "mime_type": "application/vnd.openxmlformats-officedocument.presentationml.presentation" + }, + ".odp": { + "display_name": "ODP", + "mime_type": "application/vnd.oasis.opendocument.presentation" + }, + ".pdf": { + "display_name": "PDF", + "mime_type": "application/pdf" + } +} \ No newline at end of file