mirror of
https://codeberg.org/PLG-Development/PLG-MuDiCS
synced 2026-07-06 00:47:09 +00:00
improve folder view - test with display-api
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
|
||||
export async function get_file_data(ip: string, path: string = './') {
|
||||
const options = {
|
||||
method: 'PATCH',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: `{"command":"cd ${path} && find . -maxdepth 1 -mindepth 1 -print0 | while IFS= read -r -d \'\' f; do\n typ=$(file -b --mime-type -- \"$f\")\n size=$(stat -c \'%s\' -- \"$f\")\n created=$(stat -c \'%w\' -- \"$f\")\n [ \"$created\" = \"-\" ] && created=$(stat -c \'%y\' -- \"$f\")\n jq -n --arg name \"$f\" --arg type \"$typ\" --arg size \"$size\" --arg created \"$created\" \'{name:$name, type:$type, size:($size|tonumber), created:$created}\' | tr -d \'\\n\'\n echo\n done\n"}`
|
||||
};
|
||||
console.log(request(ip, '/shellCommand', options));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function request(ip: string, api_route: string, options: { method: string, headers?: Record<string, string>, body?: string }) {
|
||||
try {
|
||||
const url = `http://${ip}:1323/api${api_route}`;
|
||||
const response = await fetch(url, options);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import type { Display, DisplayGroup } from "../types";
|
||||
import { is_selected, select, selected_display_ids } from "./select";
|
||||
|
||||
export const displays: Writable<DisplayGroup[]> = writable<DisplayGroup[]>([{
|
||||
id: crypto.randomUUID(),
|
||||
data: []
|
||||
}]);
|
||||
|
||||
export const selected_display_ids: Writable<string[]> = writable<string[]>([]);
|
||||
|
||||
|
||||
add_testing_displays();
|
||||
|
||||
function add_display(ip: string, mac: string, name: string, status: string) {
|
||||
displays.update((displays: DisplayGroup[]) => {
|
||||
@@ -17,24 +15,6 @@ function add_display(ip: string, mac: string, name: string, status: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export function select(display_id: string, new_value: boolean | null = null) {
|
||||
selected_display_ids.update((all_ids: string[]) => {
|
||||
if (all_ids.includes(display_id)) {
|
||||
const index = all_ids.indexOf(display_id);
|
||||
if (index > -1 && new_value !== true) {
|
||||
all_ids.splice(index, 1);
|
||||
}
|
||||
} else if (new_value !== false) {
|
||||
all_ids.push(display_id);
|
||||
}
|
||||
return all_ids;
|
||||
});
|
||||
}
|
||||
|
||||
export function is_selected(display_id: string, current_selected_display_ids: string[]): boolean {
|
||||
return current_selected_display_ids.includes(display_id);
|
||||
}
|
||||
|
||||
export function all_displays_of_group_selected(display_group: DisplayGroup, current_selected_displays: string[]) {
|
||||
if (display_group.data.length === 0) return false;
|
||||
for (const display of display_group.data) {
|
||||
@@ -47,7 +27,7 @@ export function all_displays_of_group_selected(display_group: DisplayGroup, curr
|
||||
|
||||
export function select_all_of_group(display_group: DisplayGroup, new_value: boolean | null = null) {
|
||||
for (const display of display_group.data) {
|
||||
select(display.id, new_value);
|
||||
select(selected_display_ids, display.id, new_value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,10 +79,12 @@ export function remove_empty_display_groups() {
|
||||
|
||||
|
||||
|
||||
|
||||
add_testing_displays();
|
||||
function add_testing_displays() {
|
||||
const names = ["Vorne Rechts", "Vorne Links", "Vorne Mitte", "Fernseher Rechts", "Fernseher Bühne", "UIUIUIUIUIUIUISEHRLANGERTEXT DER IST WIRKLICH LANG, DER TEXT, so lang, dass er wirklich nirgendswo hinpasst, nichtmal da oben /\\"];
|
||||
for (const name of names) {
|
||||
add_display("192.168.1.42", "00:1A:2B:3C:4D:5E", name, "Offline");
|
||||
}
|
||||
// const names = ["Vorne Rechts", "Vorne Links", "Vorne Mitte", "Fernseher Rechts", "Fernseher Bühne", "UIUIUIUIUIUIUISEHRLANGERTEXT DER IST WIRKLICH LANG, DER TEXT, so lang, dass er wirklich nirgendswo hinpasst, nichtmal da oben /\\"];
|
||||
// for (const name of names) {
|
||||
// add_display("127.0.0.1", "00:1A:2B:3C:4D:5E", name, "Offline");
|
||||
// }
|
||||
|
||||
add_display("127.0.0.1", "00:1A:2B:3C:4D:5E", "Test", "Offline")
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import type { FolderElement } from "../types";
|
||||
import { displays } from "./displays";
|
||||
import { selected_display_ids, selected_file_ids } from "./select";
|
||||
import { get_file_data } from "../api_handler";
|
||||
|
||||
export const all_files: Writable<Record<string, Record<string, FolderElement[]>>> = writable<Record<string, Record<string, FolderElement[]>>>({});
|
||||
// {
|
||||
// path: {
|
||||
// display_id: FolderElement[]
|
||||
// ...
|
||||
// },
|
||||
// path2: {
|
||||
// display_id: FolderElement[]
|
||||
// ...
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
|
||||
export const current_file_path: Writable<string> = writable<string>('/');
|
||||
|
||||
|
||||
export function change_file_path(new_path: string) {
|
||||
current_file_path.update(() => {
|
||||
return new_path;
|
||||
});
|
||||
selected_file_ids.update(() => {
|
||||
return [];
|
||||
})
|
||||
}
|
||||
|
||||
export function get_display_ids_where_file_is_missing(path: string, file: FolderElement, selected_display_ids: string[], all_files: Record<string, Record<string, FolderElement[]>>): string[][] {
|
||||
if (!all_files.hasOwnProperty(path)) return [];
|
||||
const missing: string[] = [];
|
||||
const colliding: string[] = [];
|
||||
Display:
|
||||
for (const selected_display_id of selected_display_ids) {
|
||||
if (!all_files[path].hasOwnProperty(selected_display_id)) {
|
||||
missing.push(selected_display_id);
|
||||
continue;
|
||||
}
|
||||
for (const folder_element of all_files[path][selected_display_id]) {
|
||||
if (folder_element.name === file.name) {
|
||||
if (folder_element.hash !== file.hash) {
|
||||
colliding.push(selected_display_id);
|
||||
}
|
||||
continue Display;
|
||||
}
|
||||
}
|
||||
missing.push(selected_display_id);
|
||||
}
|
||||
return [missing, colliding];
|
||||
}
|
||||
|
||||
|
||||
export function updates_files_on_display(display_id: string, new_folder_elements: FolderElement[], file_path: string) {
|
||||
all_files.update((files) => {
|
||||
if (!files.hasOwnProperty(file_path)) {
|
||||
files[file_path] = {};
|
||||
}
|
||||
for (const new_folder_element of new_folder_elements) {
|
||||
new_folder_element.id = crypto.randomUUID();
|
||||
}
|
||||
files[file_path][display_id] = new_folder_elements;
|
||||
return files;
|
||||
});
|
||||
}
|
||||
|
||||
function get_files_on_all_displays() {
|
||||
for (const display_group of get(displays)) {
|
||||
for (const display of display_group.data) {
|
||||
get_file_data(display.ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function get_current_folder_elements(all_files: Record<string, Record<string, FolderElement[]>>, current_file_path: string, selected_display_ids: string[]) {
|
||||
if (!all_files.hasOwnProperty(current_file_path)) {
|
||||
get_files_on_all_displays();
|
||||
return [];
|
||||
}
|
||||
const files_on_display_array = all_files[current_file_path];
|
||||
const files: FolderElement[] = [];
|
||||
for (const key of Object.keys(files_on_display_array)) {
|
||||
if (selected_display_ids.includes(key)) {
|
||||
FileOnDisplay:
|
||||
for (const file_on_display of files_on_display_array[key]) {
|
||||
for (const existing_file of files) {
|
||||
if (file_on_display.name === existing_file.name) {
|
||||
if (file_on_display.hash === existing_file.hash) {
|
||||
continue FileOnDisplay;
|
||||
}
|
||||
}
|
||||
}
|
||||
files.push(file_on_display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sort_files(files);
|
||||
}
|
||||
|
||||
function sort_files(files: FolderElement[]) {
|
||||
files.sort((a, b) => {
|
||||
const isDirA = a.type === 'inode/directory';
|
||||
const isDirB = b.type === 'inode/directory';
|
||||
|
||||
// Ordner zuerst
|
||||
if (isDirA && !isDirB) return -1;
|
||||
if (!isDirA && isDirB) return 1;
|
||||
|
||||
// Danach alphabetisch nach name (case-insensitive)
|
||||
const nameCompare = a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
|
||||
if (nameCompare !== 0) return nameCompare;
|
||||
|
||||
// Wenn name gleich, absteigend nach date_created
|
||||
return b.date_created.getTime() - a.date_created.getTime();
|
||||
});
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
add_test_files();
|
||||
export function add_test_files() {
|
||||
// updates_files_on_display(get(displays)[0].data[0].id, [
|
||||
// {
|
||||
// hash: "ok",
|
||||
// thumbnail: null,
|
||||
// name: "ok.png",
|
||||
// type: "image/png",
|
||||
// date_created: new Date("2022-09-10"),
|
||||
// size: "32 KB"
|
||||
// },
|
||||
// {
|
||||
// hash: "schön",
|
||||
// thumbnail: null,
|
||||
// name: "schön.png",
|
||||
// type: "image/png",
|
||||
// date_created: new Date("2023-09-10"),
|
||||
// size: "2 GB"
|
||||
// },
|
||||
// {
|
||||
// hash: "lang",
|
||||
// thumbnail: null,
|
||||
// name: "langer Bildname der wirklich gar nicht mehr aufhört, sodass man mal gut testen kann, wie die UI darauf reagiert.odp",
|
||||
// type: "application/vnd.oasis.opendocument.presentation",
|
||||
// date_created: new Date("2028-09-10"),
|
||||
// size: "324 KB"
|
||||
// },
|
||||
// {
|
||||
// hash: "Schön hier aber waren Sie mal in Baden Würtemberg?",
|
||||
// thumbnail: null,
|
||||
// name: "Schön hier aber waren Sie mal in Baden Würtemberg?.mp4",
|
||||
// type: "video/mp4",
|
||||
// date_created: new Date("2024-09-10"),
|
||||
// size: "32 KB"
|
||||
// },
|
||||
// {
|
||||
// hash: "lan4234g",
|
||||
// thumbnail: null,
|
||||
// name: "Ein schöner Ordner",
|
||||
// type: 'inode/directory',
|
||||
// date_created: new Date("2025-01-02"),
|
||||
// size: "324 TB"
|
||||
// },
|
||||
// {
|
||||
// hash: "Schön hi23424er aber waren Sie mal in Baden Würtemberg?",
|
||||
// thumbnail: null,
|
||||
// name: "Ein hässlicher Ordner",
|
||||
// type: 'inode/directory',
|
||||
// date_created: new Date("2025-10-22"),
|
||||
// size: "1 B"
|
||||
// },
|
||||
// ], '/');
|
||||
|
||||
|
||||
// updates_files_on_display(get(displays)[0].data[0].id, [
|
||||
// {
|
||||
// hash: "ok",
|
||||
// thumbnail: null,
|
||||
// name: "ok.png",
|
||||
// type: "image/png",
|
||||
// date_created: new Date("2022-09-10"),
|
||||
// size: "32 KB"
|
||||
// },
|
||||
// {
|
||||
// hash: "schön",
|
||||
// thumbnail: null,
|
||||
// name: "schön.png",
|
||||
// type: "image/png",
|
||||
// date_created: new Date("2023-09-10"),
|
||||
// size: "2 GB"
|
||||
// },
|
||||
// ], '/Ein hässlicher Ordner/');
|
||||
|
||||
// updates_files_on_display(get(displays)[0].data[0].id, [
|
||||
// {
|
||||
// hash: "nö",
|
||||
// thumbnail: null,
|
||||
// name: "nö.png",
|
||||
// type: "image/png",
|
||||
// date_created: new Date("2022-09-10"),
|
||||
// size: "32 KB"
|
||||
// },
|
||||
// {
|
||||
// hash: "na gut",
|
||||
// thumbnail: null,
|
||||
// name: "na gut.png",
|
||||
// type: "image/png",
|
||||
// date_created: new Date("2023-09-10"),
|
||||
// size: "2 GB"
|
||||
// },
|
||||
// ], '/Ein schöner Ordner/');
|
||||
|
||||
|
||||
|
||||
// updates_files_on_display(get(displays)[0].data[1].id, [
|
||||
// {
|
||||
// hash: "okk",
|
||||
// thumbnail: null,
|
||||
// name: "ok.png",
|
||||
// type: "image/png",
|
||||
// date_created: new Date("2022-09-10"),
|
||||
// size: "32 KB"
|
||||
// },
|
||||
// {
|
||||
// hash: "schön",
|
||||
// thumbnail: null,
|
||||
// name: "schön.png",
|
||||
// type: "image/png",
|
||||
// date_created: new Date("2023-09-10"),
|
||||
// size: "2 GB"
|
||||
// },], '/');
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
|
||||
export const selected_file_ids: Writable<string[]> = writable<string[]>([]);
|
||||
export const selected_display_ids: Writable<string[]> = writable<string[]>([]);
|
||||
|
||||
|
||||
export function select(selected_ids: Writable<string[]>, id: string, new_value: boolean | null = null) {
|
||||
selected_ids.update((all_ids: string[]) => {
|
||||
if (all_ids.includes(id)) {
|
||||
const index = all_ids.indexOf(id);
|
||||
if (index > -1 && new_value !== true) {
|
||||
all_ids.splice(index, 1);
|
||||
}
|
||||
} else if (new_value !== false) {
|
||||
all_ids.push(id);
|
||||
}
|
||||
return all_ids;
|
||||
});
|
||||
}
|
||||
|
||||
export function is_selected(id: string, selected_ids: string[]): boolean {
|
||||
return selected_ids.includes(id);
|
||||
}
|
||||
@@ -1,36 +1,51 @@
|
||||
import type { NumericRange } from "@sveltejs/kit";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
|
||||
const screen_height_step_size = 5;
|
||||
export const dnd_flip_duration_ms = 300;
|
||||
|
||||
const min_display_screen_height = 15;
|
||||
const max_display_screen_height = 40;
|
||||
export const display_screen_height: Writable<number> = writable<number>(25);
|
||||
const heights_options: Record<string, { min: number; max: number; step: number }> = {
|
||||
display: {
|
||||
min: 15,
|
||||
max: 40,
|
||||
step: 5,
|
||||
},
|
||||
file: {
|
||||
min: 10,
|
||||
max: 20,
|
||||
step: 5,
|
||||
}
|
||||
}
|
||||
|
||||
export const current_height: Writable<Record<string, number>> = writable<Record<string, number>>({
|
||||
display: 25,
|
||||
file: 10,
|
||||
});
|
||||
|
||||
|
||||
export const is_group_drag: Writable<boolean> = writable<boolean>(false);
|
||||
export const is_display_drag: Writable<boolean> = writable<boolean>(false);
|
||||
|
||||
export const pinned_display_id: Writable<string | null> = writable<string | null>(null);
|
||||
|
||||
export function change_display_screen_height(factor: number) {
|
||||
display_screen_height.update((current_height) => {
|
||||
const new_size = current_height + (factor * screen_height_step_size);
|
||||
if (new_size > max_display_screen_height || new_size < min_display_screen_height) {
|
||||
return current_height;
|
||||
} else {
|
||||
return new_size;
|
||||
}
|
||||
|
||||
export function change_height(key: 'display' | 'file', factor: number) {
|
||||
current_height.update((height) => {
|
||||
height[key] = next_height_step_size(key, height, factor) || height[key];
|
||||
return height;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function next_step_possible(current_height: number, factor: number) {
|
||||
const new_size = current_height + (factor * screen_height_step_size);
|
||||
return new_size > max_display_screen_height || new_size < min_display_screen_height;
|
||||
export function next_height_step_size(key: 'display' | 'file', current_height_array: Record<string, number>, factor: number): number {
|
||||
const new_size = current_height_array[key] + (factor * heights_options[key].step);
|
||||
if (new_size > heights_options[key].max || new_size < heights_options[key].min) {
|
||||
return 0;
|
||||
} else {
|
||||
return new_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export function get_selectable_color_classes(
|
||||
selected: boolean,
|
||||
returning_classes: { bg?: boolean; hover?: boolean; active?: boolean; text?: boolean } = {},
|
||||
|
||||
@@ -1,4 +1,59 @@
|
||||
import type { X } from "lucide-svelte";
|
||||
import { FileBox, FileImage, FileVideoCamera, ImagePlay, type X } from "lucide-svelte";
|
||||
|
||||
export type SupportedFileType = {
|
||||
display_name:
|
||||
string; mime_type:
|
||||
string; icon: typeof X;
|
||||
};
|
||||
|
||||
export const supported_file_types: Record<string, SupportedFileType> = {
|
||||
'.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 type FolderElement = {
|
||||
id?: string;
|
||||
hash: string;
|
||||
thumbnail: Blob | null;
|
||||
name: string;
|
||||
type: string;
|
||||
date_created: Date;
|
||||
size: string;
|
||||
}
|
||||
|
||||
|
||||
export type Display = {
|
||||
id: string;
|
||||
@@ -20,5 +75,4 @@ export type MenuOption = {
|
||||
class?: string;
|
||||
on_select?: () => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user