Remove some event dispatchers

This commit is contained in:
2025-10-14 00:03:48 +02:00
parent 75d9ed3023
commit b409ff009d
23 changed files with 165 additions and 316 deletions

View File

@@ -6,7 +6,7 @@ import Breadcrumbs from "./Breadcrumbs.svelte";
import DetailsWindow from "./DetailsWindow.svelte";
import FilePreview from "./viewers/FilePreview.svelte";
import FSUploadWidget from "./upload_widget/FSUploadWidget.svelte";
import { fs_download, type FSPath } from "lib/FilesystemAPI.svelte";
import { type FSPath } from "lib/FilesystemAPI.svelte";
import { FSNavigator } from "./FSNavigator"
import { css_from_path } from "filesystem/edit_window/Branding";
import AffiliatePrompt from "user_home/AffiliatePrompt.svelte";
@@ -18,6 +18,7 @@ let upload_widget: FSUploadWidget = $state()
let details_visible = $state(false)
let edit_window: EditWindow = $state()
let edit_visible = $state(false)
let details_window: DetailsWindow = $state()
const nav = $state(new FSNavigator(true))
@@ -75,16 +76,11 @@ const keydown = (e: KeyboardEvent) => {
}
break;
case "s":
fs_download(nav.base)
nav.base.download()
break;
case "r":
nav.shuffle = !nav.shuffle
break;
case "f": // F fullscreen
if (toolbar) {
toolbar.toggle_fullscreen()
}
break
case "a":
case "ArrowLeft":
nav.open_sibling(-1)
@@ -145,9 +141,7 @@ const keydown = (e: KeyboardEvent) => {
nav={nav}
upload_widget={upload_widget}
edit_window={edit_window}
on:open_sibling={e => nav.open_sibling(e.detail)}
on:download={() => fs_download(nav.base)}
on:details={() => details_visible = !details_visible}
details_window={details_window}
/>
</div>
@@ -157,11 +151,10 @@ const keydown = (e: KeyboardEvent) => {
bind:details_visible={details_visible}
edit_window={edit_window}
bind:edit_visible={edit_visible}
on:download={() => fs_download(nav.base)}
/>
</div>
<DetailsWindow nav={nav} bind:visible={details_visible} />
<DetailsWindow nav={nav} bind:this={details_window} bind:visible={details_visible} />
<EditWindow nav={nav} bind:this={edit_window} bind:visible={edit_visible} />

View File

@@ -1,5 +1,4 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { copy_text } from "util/Util";
import FileStats from "./FileStats.svelte";
import type { FSNavigator } from "./FSNavigator";
@@ -8,8 +7,6 @@ import { fs_share_url, path_is_shared } from "lib/FilesystemAPI.svelte";
import ShareDialog from "./ShareDialog.svelte";
import { bookmark_add, bookmark_del, bookmarks_store, is_bookmark } from "lib/Bookmarks";
let dispatch = createEventDispatcher()
let {
nav = $bindable(),
details_visible = $bindable(false),
@@ -53,7 +50,7 @@ export const copy_link = () => {
</button>
</div>
<button onclick={() => dispatch("download")}>
<button onclick={() => $nav.base.download()}>
<i class="icon">save</i>
<span>Download</span>
</button>

View File

@@ -33,12 +33,12 @@ const pick_image = (type: string) => {
picking = type
picker.open(file.path)
}
const handle_picker = async (e: CustomEvent<FSNode[]>) => {
if (e.detail.length !== 1) {
const handle_picker = async (nodes: FSNode[]) => {
if (nodes.length !== 1) {
alert("Please select one file")
return
}
let f = e.detail[0]
let f = nodes[0]
if (fs_node_type(f) !== "image") {
alert("Please select an image file")
@@ -52,7 +52,7 @@ const handle_picker = async (e: CustomEvent<FSNode[]>) => {
if (!f.is_shared()) {
try {
f = await fs_update(
e.detail[0].path,
nodes[0].path,
{link_permissions: {read: true} as FSPermissions},
)
} catch (err) {
@@ -186,7 +186,7 @@ run(() => {
</div>
</fieldset>
<FilePicker bind:this={picker} on:files={handle_picker}/>
<FilePicker bind:this={picker} callback={handle_picker}/>
<style>
input[type="color"] {

View File

@@ -4,7 +4,7 @@ import type { FSNavigator } from "filesystem/FSNavigator";
import Button from "layout/Button.svelte";
import Dialog from "layout/Dialog.svelte";
import { bookmark_add, bookmark_del, bookmarks_store, is_bookmark } from "lib/Bookmarks";
import { fs_delete, fs_download, type FSNode } from "lib/FilesystemAPI.svelte";
import { fs_delete, type FSNode } from "lib/FilesystemAPI.svelte";
import { loading_finish, loading_start } from "lib/Loading";
import { tick } from "svelte";
@@ -46,7 +46,7 @@ const delete_node = async () => {
<Dialog bind:this={dialog}>
<div class="menu">
<Button click={() => {dialog.close(); fs_download(node)}} icon="save" label="Download"/>
<Button click={() => {dialog.close(); node.download()}} icon="save" label="Download"/>
{#if node !== null && is_bookmark($bookmarks_store, node.id)}
<Button click={() => {dialog.close(); bookmark_del(node.id)}} icon="bookmark_remove" label="Remove bookmark"/>
{:else}

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { createEventDispatcher, onMount } from "svelte"
import { onMount } from "svelte"
import ListView from "./ListView.svelte"
import GalleryView from "./GalleryView.svelte"
import CompactView from "./CompactView.svelte"
@@ -11,13 +11,16 @@ import { FileAction, type FileActionHandler } from "./FileManagerLib";
let nav = $state(new FSNavigator(false))
let modal: Modal = $state()
let dispatch = createEventDispatcher()
let directory_view = $state("")
let large_icons = $state(false)
let show_hidden = $state(false)
let { select_multiple = false }: {
select_multiple?: boolean;
let {
callback,
select_multiple = false
}: {
callback: (files: FSNode[]) => void
select_multiple?: boolean
} = $props();
export const open = (path: string) => {
@@ -113,7 +116,7 @@ let done = () => {
}
if (selected_files.length > 0) {
dispatch("files", selected_files)
callback(selected_files)
}
modal.hide()
}

View File

@@ -172,7 +172,7 @@ const leave_confirmation = (e: BeforeUnloadEvent) => {
<div class="body">
{#each upload_queue as job}
{#if job.status !== "finished"}
<UploadProgress bind:this={job.component} job={job} on:finished={finish_upload}/>
<UploadProgress bind:this={job.component} job={job} finish={finish_upload}/>
{/if}
{/each}
</div>

View File

@@ -1,20 +1,19 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { upload_file } from "./UploadFunc";
import ProgressBar from "util/ProgressBar.svelte";
import Button from "layout/Button.svelte"
import type { UploadJob } from "./FSUploadWidget.svelte";
let dispatch = createEventDispatcher()
let {
job = $bindable(),
total = $bindable(0),
loaded = $bindable(0)
loaded = $bindable(0),
finish,
}: {
job: UploadJob;
total?: number;
loaded?: number;
job: UploadJob
total?: number
loaded?: number
finish: () => void
} = $props();
let error_code = $state("")
@@ -31,7 +30,7 @@ export const start = () => {
},
async () => {
job.status = "finished"
dispatch("finished")
finish()
},
(code, message) => {
if (job.status === "finished") {
@@ -59,7 +58,7 @@ const cancel = () => {
}
xhr = null
dispatch("finished")
finish()
}
</script>

View File

@@ -1,39 +1,40 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import IconBlock from "layout/IconBlock.svelte";
import { fs_thumbnail_url } from "lib/FilesystemAPI.svelte";
import { fs_thumbnail_url, FSNode } from "lib/FilesystemAPI.svelte";
import TextBlock from "layout/TextBlock.svelte"
import { formatDataVolume, formatDate } from "util/Formatting";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher()
let { nav, children }: {
nav: FSNavigator;
children?: import('svelte').Snippet;
let {
node,
open_details,
children,
}: {
node: FSNode
open_details: () => void
children?: import('svelte').Snippet
} = $props();
</script>
{@render children?.()}
<h1>{$nav.base.name}</h1>
<h1>{node.name}</h1>
<IconBlock icon_href={fs_thumbnail_url($nav.base.path, 256, 256)}>
Type: {$nav.base.file_type}<br/>
Size: {formatDataVolume($nav.base.file_size, 3)}<br/>
Upload date: {formatDate($nav.base.created, true, true, false)}
<IconBlock icon_href={fs_thumbnail_url(node.path, 256, 256)}>
Type: {node.file_type}<br/>
Size: {formatDataVolume(node.file_size, 3)}<br/>
Upload date: {formatDate(node.created, true, true, false)}
<hr/>
<button class="button_highlight" onclick={() => {dispatch("download")}}>
<button class="button_highlight" onclick={() => node.download()}>
<i class="icon">download</i>
<span>Download</span>
</button>
<button onclick={() => {dispatch("details")}}>
<button onclick={() => open_details()}>
<i class="icon">help</i>
<span>Details</span>
</button>
</IconBlock>
{#if $nav.base.name === ".search_index.gz"}
{#if node.name === ".search_index.gz"}
<TextBlock>
<p>
Congratulations! You have found the search index. One of the

View File

@@ -15,11 +15,18 @@ import CustomBanner from "./CustomBanner.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
import FsUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte";
import EditWindow from "filesystem/edit_window/EditWindow.svelte";
import DetailsWindow from "filesystem/DetailsWindow.svelte";
let { nav, upload_widget, edit_window }: {
nav: FSNavigator;
upload_widget: FsUploadWidget;
edit_window: EditWindow;
let {
nav,
upload_widget,
edit_window,
details_window,
}: {
nav: FSNavigator
upload_widget: FsUploadWidget
edit_window: EditWindow
details_window: DetailsWindow
} = $props();
let viewer: any = $state()
@@ -89,23 +96,23 @@ export const seek = (delta: number) => {
{:else if viewer_type === "image"}
<Image nav={nav} bind:this={viewer}/>
{:else if viewer_type === "video"}
<Video nav={nav} bind:this={viewer} on:open_sibling/>
<Video node={$nav.base} bind:this={viewer} open_sibling={(d) => nav.open_sibling(d)}/>
{:else if viewer_type === "pdf"}
<Pdf nav={nav} bind:this={viewer}/>
<Pdf node={$nav.base} bind:this={viewer}/>
{:else if viewer_type === "text"}
<Text nav={nav} bind:this={viewer}>
<Text node={$nav.base} bind:this={viewer}>
<CustomBanner path={$nav.path}/>
</Text>
{:else if viewer_type === "torrent"}
<Torrent nav={nav} bind:this={viewer} on:download>
<Torrent node={$nav.base} bind:this={viewer}>
<CustomBanner path={$nav.path}/>
</Torrent>
{:else if viewer_type === "zip"}
<Zip nav={nav} bind:this={viewer} on:download>
<Zip node={$nav.base} bind:this={viewer}>
<CustomBanner path={$nav.path}/>
</Zip>
{:else}
<File nav={nav} on:download on:details>
<File node={$nav.base} open_details={() => details_window.toggle()}>
<CustomBanner path={$nav.path}/>
</File>
{/if}

View File

@@ -1,10 +1,8 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { swipe_nav } from "lib/SwipeNavigate";
import { fs_path_url } from "lib/FilesystemAPI.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher();
import { loading_finish, loading_start } from "lib/Loading";
let { nav }: {
nav: FSNavigator;
@@ -18,7 +16,7 @@ let swipe_prev = $state(true)
let swipe_next = $state(true)
export const update = async () => {
dispatch("loading", true)
loading_start()
// Figure out if there are previous or next files. If not then we disable
// swiping controls in that direction
@@ -32,7 +30,7 @@ export const update = async () => {
}
}
const on_load = () => dispatch("loading", false)
const on_load = () => loading_finish()
const mousedown = (e: MouseEvent) => {
if (!dragging && e.which === 1 && zoom) {

View File

@@ -1,15 +1,12 @@
<script lang="ts">
import { fs_path_url } from "lib/FilesystemAPI.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
import { fs_path_url, FSNode } from "lib/FilesystemAPI.svelte";
let { nav }: {
nav: FSNavigator;
} = $props();
let { node }: { node: FSNode } = $props();
</script>
<iframe
class="container"
src={"/res/misc/pdf-viewer/web/viewer.html?file="+fs_path_url($nav.base.path)}
src={"/res/misc/pdf-viewer/web/viewer.html?file="+fs_path_url(node.path)}
title="PDF viewer">
</iframe>

View File

@@ -1,31 +1,33 @@
<script lang="ts">
import { tick } from "svelte";
import { fs_path_url, type FSNode } from "lib/FilesystemAPI.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
let { nav, children }: {
nav: FSNavigator;
let {
node,
children
}: {
node: FSNode
children?: import('svelte').Snippet;
} = $props();
let text_type = $state("text")
export const update = () => {
console.debug("Loading text file", nav.base.name)
console.debug("Loading text file", node.name)
if (nav.base.file_size > 1 << 21) { // File larger than 2 MiB
if (node.file_size > 1 << 21) { // File larger than 2 MiB
text_pre.innerText = "File is too large to view online.\nPlease download and view it locally."
return
}
if (
nav.base.file_type.startsWith("text/markdown") ||
nav.base.name.endsWith(".md") ||
nav.base.name.endsWith(".markdown")
node.file_type.startsWith("text/markdown") ||
node.name.endsWith(".md") ||
node.name.endsWith(".markdown")
) {
markdown(nav.base)
markdown(node)
} else {
text(nav.base)
text(node)
}
}

View File

@@ -13,21 +13,20 @@ export type TorrentFile = {
};
</script>
<script lang="ts">
import { createEventDispatcher } from "svelte";
import Magnet from "icons/Magnet.svelte";
import { formatDate } from "util/Formatting"
import TorrentItem from "./TorrentItem.svelte"
import IconBlock from "layout/IconBlock.svelte";
import TextBlock from "layout/TextBlock.svelte"
import { fs_node_icon, fs_path_url } from "lib/FilesystemAPI.svelte";
import { fs_node_icon, fs_path_url, FSNode } from "lib/FilesystemAPI.svelte";
import CopyButton from "layout/CopyButton.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
import { loading_finish, loading_start } from "lib/Loading";
let dispatch = createEventDispatcher()
let { nav, children }: {
nav: FSNavigator;
let {
node,
children
}: {
node: FSNode;
children?: import('svelte').Snippet;
} = $props();
@@ -36,7 +35,7 @@ let status = $state("loading")
export const update = async () => {
try {
loading_start()
let resp = await fetch(fs_path_url(nav.base.path)+"?torrent_info")
let resp = await fetch(fs_path_url(node.path)+"?torrent_info")
if (resp.status >= 400) {
let json = await resp.json()
@@ -73,9 +72,9 @@ let magnet = $state("")
{@render children?.()}
<h1>{$nav.base.name}</h1>
<h1>{node.name}</h1>
<IconBlock icon_href={fs_node_icon($nav.base, 256, 256)}>
<IconBlock icon_href={fs_node_icon(node, 256, 256)}>
{#if status === "finished"}
Created by: {torrent.created_by}<br/>
Comment: {torrent.comment}<br/>
@@ -96,7 +95,7 @@ let magnet = $state("")
Torrent file could not be parsed. It may be corrupted.
</p>
{/if}
<button onclick={() => {dispatch("download")}} class="button">
<button onclick={() => node.download()} class="button">
<i class="icon">download</i>
<span>Download torrent file</span>
</button>

View File

@@ -1,12 +1,14 @@
<script lang="ts">
import { onMount, createEventDispatcher, tick } from "svelte";
import { onMount, tick } from "svelte";
import { video_position } from "lib/VideoPosition";
import { fs_path_url } from "lib/FilesystemAPI.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher()
import { fs_path_url, FSNode } from "lib/FilesystemAPI.svelte";
let { nav }: {
nav: FSNavigator;
let {
node,
open_sibling,
}: {
node: FSNode
open_sibling: (delta: number) => void
} = $props();
// Used to detect when the file path changes
@@ -21,20 +23,20 @@ let loop = $state(false)
export const update = async () => {
if (media_session) {
navigator.mediaSession.metadata = new MediaMetadata({
title: nav.base.name,
title: node.name,
artist: "pixeldrain",
album: "unknown",
});
console.debug("Updating media session")
}
loop = nav.base.name.includes(".loop.")
loop = node.name.includes(".loop.")
// When the component receives a new ID the video track does not
// automatically start playing the new video. So we use this little hack to
// make sure that the video is unloaded and loaded when the ID changes
if (nav.base.path != last_path) {
last_path = nav.base.path
if (node.path != last_path) {
last_path = node.path
loaded = false
await tick()
loaded = true
@@ -51,8 +53,7 @@ export const toggle_fullscreen = () => {
}
}
export const seek = delta => {
export const seek = (delta: number) => {
// fastseek can be pretty imprecise, so we don't use it for small seeks
// below 5 seconds
if (player.fastSeek && delta > 5) {
@@ -68,8 +69,8 @@ onMount(() => {
navigator.mediaSession.setActionHandler('play', () => player.play());
navigator.mediaSession.setActionHandler('pause', () => player.pause());
navigator.mediaSession.setActionHandler('stop', () => player.pause());
navigator.mediaSession.setActionHandler('previoustrack', () => dispatch("open_sibling", -1));
navigator.mediaSession.setActionHandler('nexttrack', () => dispatch("open_sibling", 1));
navigator.mediaSession.setActionHandler('previoustrack', () => open_sibling(-1));
navigator.mediaSession.setActionHandler('nexttrack', () => open_sibling(1));
}
})
@@ -85,9 +86,9 @@ const video_keydown = (e: KeyboardEvent) => {
<div class="container">
{#if
$nav.base.file_type === "video/x-matroska" ||
$nav.base.file_type === "video/quicktime" ||
$nav.base.file_type === "video/x-ms-asf"}
node.file_type === "video/x-matroska" ||
node.file_type === "video/quicktime" ||
node.file_type === "video/x-ms-asf"}
<div class="compatibility_warning">
This video file type is not compatible with every web
browser. If the video fails to play you can try downloading
@@ -108,16 +109,16 @@ const video_keydown = (e: KeyboardEvent) => {
onpause={() => playing = false}
onplay={() => playing = true}
onkeydown={video_keydown}
use:video_position={() => $nav.base.sha256_sum.substring(0, 8)}
use:video_position={() => node.sha256_sum.substring(0, 8)}
>
<source src={fs_path_url($nav.base.path)} type={$nav.base.file_type} />
<source src={fs_path_url(node.path)} type={node.file_type} />
</video>
{/if}
</div>
<div class="controls">
<div class="spacer"></div>
<button onclick={() => dispatch("open_sibling", -1)}>
<button onclick={() => open_sibling(-1)}>
<i class="icon">skip_previous</i>
</button>
<button onclick={() => seek(-10)}>
@@ -133,7 +134,7 @@ const video_keydown = (e: KeyboardEvent) => {
<button onclick={() => seek(10)}>
<i class="icon">forward_10</i>
</button>
<button onclick={() => dispatch("open_sibling", 1)}>
<button onclick={() => open_sibling(1)}>
<i class="icon">skip_next</i>
</button>
<div style="width: 16px; height: 8px;"></div>

View File

@@ -8,19 +8,18 @@ export type ZipEntry = {
};
</script>
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { formatDataVolume, formatDate } from "util/Formatting"
import ZipItem from "filesystem/viewers/ZipItem.svelte";
import IconBlock from "layout/IconBlock.svelte";
import TextBlock from "layout/TextBlock.svelte"
import { fs_node_icon, fs_path_url } from "lib/FilesystemAPI.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
import { fs_node_icon, fs_path_url, FSNode } from "lib/FilesystemAPI.svelte";
import { loading_finish, loading_start } from "lib/Loading";
let dispatch = createEventDispatcher()
let { nav, children }: {
nav: FSNavigator;
let {
node,
children,
}: {
node: FSNode;
children?: import('svelte').Snippet;
} = $props();
@@ -33,7 +32,7 @@ let archive_type = $state("")
let truncated = $state(false)
export const update = async () => {
if (nav.base.file_type === "application/x-7z-compressed") {
if (node.file_type === "application/x-7z-compressed") {
archive_type = "7z"
} else {
archive_type = ""
@@ -42,7 +41,7 @@ export const update = async () => {
try {
loading_start()
status = "loading"
let resp = await fetch(fs_path_url(nav.base.path)+"?zip_info")
let resp = await fetch(fs_path_url(node.path)+"?zip_info")
if (resp.status >= 400) {
status = "parse_failed"
@@ -56,13 +55,13 @@ export const update = async () => {
if (zip.properties !== undefined) {
if (zip.properties.includes("read_individual_files")) {
// Set the download URL for each file in the zip
recursive_set_url(fs_path_url(nav.base.path)+"?zip_file=", zip)
recursive_set_url(fs_path_url(node.path)+"?zip_file=", zip)
}
truncated = zip.properties.includes("truncated")
}
uncomp_size = recursive_size(zip)
comp_ratio = (uncomp_size / nav.base.file_size)
comp_ratio = (uncomp_size / node.file_size)
status = "finished"
} catch (err) {
console.error(err)
@@ -98,22 +97,22 @@ const recursive_size = (file: ZipEntry) => {
{@render children?.()}
<h1>{$nav.base.name}</h1>
<h1>{node.name}</h1>
<IconBlock icon_href={fs_node_icon($nav.base, 256, 256)}>
<IconBlock icon_href={fs_node_icon(node, 256, 256)}>
{#if archive_type === "7z"}
This is a 7-zip archive. You will need
<a href="https://www.7-zip.org/">7-zip</a> or compatible software to
extract it<br/>
{/if}
Compressed size: {formatDataVolume($nav.base.file_size, 3)}<br/>
Compressed size: {formatDataVolume(node.file_size, 3)}<br/>
{#if !truncated}
Uncompressed size: {formatDataVolume(zip.size, 3)} (Ratio: {comp_ratio.toFixed(2)}x)<br/>
{/if}
Uploaded on: {formatDate($nav.base.created, true, true, true)}
Uploaded on: {formatDate(node.created, true, true, true)}
<br/>
<button class="button_highlight" onclick={() => {dispatch("download")}}>
<button class="button_highlight" onclick={() => node.download()}>
<i class="icon">download</i>
<span>Download</span>
</button>