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

@@ -4,13 +4,16 @@ import Expandable from "util/Expandable.svelte";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
let { report, ip_report_count } = $props(); let {
report,
ip_report_count
} = $props();
let preview = $state(false) let preview = $state(false)
let can_grant = $derived(report.status !== "granted") let can_grant = $derived(report.status !== "granted")
let can_reject = $derived(report.status !== "rejected") let can_reject = $derived(report.status !== "rejected")
let set_status = async (action, report_type) => { let set_status = async (action: string, report_type: string) => {
dispatch("resolve_report", {action: action, report_type: report_type}) dispatch("resolve_report", {action: action, report_type: report_type})
} }
</script> </script>

View File

@@ -199,7 +199,7 @@ onDestroy(() => {
</div> </div>
<br/> <br/>
<ServerDiagnostics running_since={status.cpu_profile_running_since} on:refresh={() => getStats(lastOrder)}/> <ServerDiagnostics running_since={status.cpu_profile_running_since} refresh={() => getStats(lastOrder)}/>
<section> <section>
<h3>Process stats</h3> <h3>Process stats</h3>

View File

@@ -1,14 +1,15 @@
<script lang="ts"> <script lang="ts">
import { get_endpoint } from "lib/PixeldrainAPI"; import { get_endpoint } from "lib/PixeldrainAPI";
import { createEventDispatcher, onMount } from "svelte"; import { onMount } from "svelte";
import { formatDuration } from "util/Formatting"; import { formatDuration } from "util/Formatting";
let dispatch = createEventDispatcher()
interface Props { let {
running_since?: string; running_since = "",
} refresh,
}: {
let { running_since = "" }: Props = $props(); running_since?: string
refresh?: () => void
} = $props();
let profile_running = $derived(running_since != "0001-01-01T00:00:00Z" && running_since != "") let profile_running = $derived(running_since != "0001-01-01T00:00:00Z" && running_since != "")
@@ -25,7 +26,9 @@ const start = async () => {
window.open(get_endpoint()+"/admin/cpu_profile") window.open(get_endpoint()+"/admin/cpu_profile")
} }
dispatch("refresh") if (refresh !== undefined) {
refresh()
}
} }
let interval: number let interval: number

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -172,7 +172,7 @@ const leave_confirmation = (e: BeforeUnloadEvent) => {
<div class="body"> <div class="body">
{#each upload_queue as job} {#each upload_queue as job}
{#if job.status !== "finished"} {#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} {/if}
{/each} {/each}
</div> </div>

View File

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

View File

@@ -1,39 +1,40 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from "svelte";
import IconBlock from "layout/IconBlock.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 TextBlock from "layout/TextBlock.svelte"
import { formatDataVolume, formatDate } from "util/Formatting"; import { formatDataVolume, formatDate } from "util/Formatting";
import type { FSNavigator } from "filesystem/FSNavigator";
let dispatch = createEventDispatcher() let {
node,
let { nav, children }: { open_details,
nav: FSNavigator; children,
children?: import('svelte').Snippet; }: {
node: FSNode
open_details: () => void
children?: import('svelte').Snippet
} = $props(); } = $props();
</script> </script>
{@render children?.()} {@render children?.()}
<h1>{$nav.base.name}</h1> <h1>{node.name}</h1>
<IconBlock icon_href={fs_thumbnail_url($nav.base.path, 256, 256)}> <IconBlock icon_href={fs_thumbnail_url(node.path, 256, 256)}>
Type: {$nav.base.file_type}<br/> Type: {node.file_type}<br/>
Size: {formatDataVolume($nav.base.file_size, 3)}<br/> Size: {formatDataVolume(node.file_size, 3)}<br/>
Upload date: {formatDate($nav.base.created, true, true, false)} Upload date: {formatDate(node.created, true, true, false)}
<hr/> <hr/>
<button class="button_highlight" onclick={() => {dispatch("download")}}> <button class="button_highlight" onclick={() => node.download()}>
<i class="icon">download</i> <i class="icon">download</i>
<span>Download</span> <span>Download</span>
</button> </button>
<button onclick={() => {dispatch("details")}}> <button onclick={() => open_details()}>
<i class="icon">help</i> <i class="icon">help</i>
<span>Details</span> <span>Details</span>
</button> </button>
</IconBlock> </IconBlock>
{#if $nav.base.name === ".search_index.gz"} {#if node.name === ".search_index.gz"}
<TextBlock> <TextBlock>
<p> <p>
Congratulations! You have found the search index. One of the 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 type { FSNavigator } from "filesystem/FSNavigator";
import FsUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte"; import FsUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte";
import EditWindow from "filesystem/edit_window/EditWindow.svelte"; import EditWindow from "filesystem/edit_window/EditWindow.svelte";
import DetailsWindow from "filesystem/DetailsWindow.svelte";
let { nav, upload_widget, edit_window }: { let {
nav: FSNavigator; nav,
upload_widget: FsUploadWidget; upload_widget,
edit_window: EditWindow; edit_window,
details_window,
}: {
nav: FSNavigator
upload_widget: FsUploadWidget
edit_window: EditWindow
details_window: DetailsWindow
} = $props(); } = $props();
let viewer: any = $state() let viewer: any = $state()
@@ -89,23 +96,23 @@ export const seek = (delta: number) => {
{:else if viewer_type === "image"} {:else if viewer_type === "image"}
<Image nav={nav} bind:this={viewer}/> <Image nav={nav} bind:this={viewer}/>
{:else if viewer_type === "video"} {: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"} {:else if viewer_type === "pdf"}
<Pdf nav={nav} bind:this={viewer}/> <Pdf node={$nav.base} bind:this={viewer}/>
{:else if viewer_type === "text"} {:else if viewer_type === "text"}
<Text nav={nav} bind:this={viewer}> <Text node={$nav.base} bind:this={viewer}>
<CustomBanner path={$nav.path}/> <CustomBanner path={$nav.path}/>
</Text> </Text>
{:else if viewer_type === "torrent"} {:else if viewer_type === "torrent"}
<Torrent nav={nav} bind:this={viewer} on:download> <Torrent node={$nav.base} bind:this={viewer}>
<CustomBanner path={$nav.path}/> <CustomBanner path={$nav.path}/>
</Torrent> </Torrent>
{:else if viewer_type === "zip"} {:else if viewer_type === "zip"}
<Zip nav={nav} bind:this={viewer} on:download> <Zip node={$nav.base} bind:this={viewer}>
<CustomBanner path={$nav.path}/> <CustomBanner path={$nav.path}/>
</Zip> </Zip>
{:else} {:else}
<File nav={nav} on:download on:details> <File node={$nav.base} open_details={() => details_window.toggle()}>
<CustomBanner path={$nav.path}/> <CustomBanner path={$nav.path}/>
</File> </File>
{/if} {/if}

View File

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

View File

@@ -1,15 +1,12 @@
<script lang="ts"> <script lang="ts">
import { fs_path_url } from "lib/FilesystemAPI.svelte"; import { fs_path_url, FSNode } from "lib/FilesystemAPI.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
let { nav }: { let { node }: { node: FSNode } = $props();
nav: FSNavigator;
} = $props();
</script> </script>
<iframe <iframe
class="container" 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"> title="PDF viewer">
</iframe> </iframe>

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,83 +0,0 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { formatDataVolume, formatDuration } from "util/Formatting";
import { stats } from "lib/StatsSocket"
import TextBlock from "./TextBlock.svelte"
import IconBlock from "./IconBlock.svelte";
import { user } from "lib/UserStore";
let dispatch = createEventDispatcher()
let {
file_size = 0,
file_name = "",
file_type = "",
icon_href = ""
}: {
file_size?: number;
file_name?: string;
file_type?: string;
icon_href?: string;
} = $props();
</script>
<TextBlock>
<img src="/res/img/slow_down.webp" class="header_image" alt="Yea, I'm gonna need you to slow down a bit"/>
<p>
Pixeldrain's free tier is supported by my Patrons. There's only so much
that you can do with the budget they provide.
{formatDataVolume($stats.limits.transfer_limit, 3)} per day is about the
most I can give away for free while keeping it fair for everyone, and
according to our records you have already downloaded
{formatDataVolume($stats.limits.transfer_limit_used, 3)}.
</p>
<p>
It's not that I want to withold this file from you, it's just that I
don't want pixeldrain to fall into bankruptcy like so many of the
websites that came before me. So if you really want this file you have a
few options:
</p>
<ul>
<li>
Come back tomorrow when your free transfer limit resets
</li>
<li>
Download the file at a limited rate of {formatDataVolume($stats.limits.speed_limit, 3)}/s. This will take at
least {formatDuration((file_size/($stats.limits.speed_limit))*1000, 0)}
</li>
<li>
<a href="/user/prepaid/deposit" target="_blank" class="button button_highlight">
<i class="icon">bolt</i> Get Premium
</a>
and earn my eternal gratitude
{#if $user.username === undefined || $user.username === ""}
(you will need a <a href="/register">pixeldrain account</a> to
receive the benefits)
{/if}
</li>
</ul>
</TextBlock>
<IconBlock icon_href={icon_href}>
<table>
<tbody>
<tr><td colspan="2">{file_name}</td></tr>
<tr><td>Type</td><td>{file_type}</td></tr>
<tr><td>Size</td><td>{formatDataVolume(file_size, 3)}</td></tr>
</tbody>
</table>
<button onclick={() => {dispatch("download")}}>
<i class="icon">download</i> Download
</button>
</IconBlock>
<TextBlock>
Also, I believe you have my stapler. Please give it back.
</TextBlock>
<style>
.header_image {
width: 100%;
border-radius: 8px;
}
</style>

View File

@@ -58,6 +58,22 @@ export class FSNode {
(this.user_permissions !== undefined && Object.keys(this.user_permissions).length > 0) || (this.user_permissions !== undefined && Object.keys(this.user_permissions).length > 0) ||
(this.password_permissions !== undefined && Object.keys(this.password_permissions).length > 0) (this.password_permissions !== undefined && Object.keys(this.password_permissions).length > 0)
} }
download = () => {
const a = document.createElement("a")
if (this.type === "file") {
a.href = fs_path_url(this.path) + "?attach"
a.download = this.name
} else if (this.type === "dir") {
a.href = fs_path_url(this.path) + "?bulk_download"
a.download = this.name + ".zip"
}
// You can't call .click() on an element that is not in the DOM. But
// emitting a click event works
a.dispatchEvent(new MouseEvent("click"))
}
} }
export type FSNodeProperties = { export type FSNodeProperties = {
@@ -400,19 +416,3 @@ export const fs_share_path = (path: FSNode[]): string => {
return share_url return share_url
} }
export const fs_download = (node: FSNode) => {
const a = document.createElement("a")
if (node.type === "file") {
a.href = fs_path_url(node.path) + "?attach"
a.download = node.name
} else if (node.type === "dir") {
a.href = fs_path_url(node.path) + "?bulk_download"
a.download = node.name + ".zip"
}
// You can't call .click() on an element that is not in the DOM. But
// emitting a click event works
a.dispatchEvent(new MouseEvent("click"))
}

View File

@@ -6,9 +6,8 @@ let global_index = 10000;
<script lang="ts"> <script lang="ts">
import { createBubbler, stopPropagation } from 'svelte/legacy'; import { createBubbler, stopPropagation } from 'svelte/legacy';
const bubble = createBubbler(); const bubble = createBubbler();
import { createEventDispatcher, type Snippet } from "svelte"; import { type Snippet } from "svelte";
import { fade } from "svelte/transition"; import { fade } from "svelte/transition";
import Button from "layout/Button.svelte"; import Button from "layout/Button.svelte";
@@ -42,16 +41,11 @@ const load_bg = background => {
global_index++; global_index++;
} }
const dispatch = createEventDispatcher();
export const show = () => { set_visible(true) } export const show = () => { set_visible(true) }
export const hide = () => { set_visible(false) } export const hide = () => { set_visible(false) }
export const toggle = () => { set_visible(!visible) } export const toggle = () => { set_visible(!visible) }
export const is_visible = () => { return visible } export const is_visible = () => { return visible }
export const set_visible = vis => { export const set_visible = (vis: boolean) => visible = vis
visible = vis
dispatch("is_visible", visible)
}
const keydown = (e: KeyboardEvent) => { const keydown = (e: KeyboardEvent) => {
if ((document.activeElement as any).type && (document.activeElement as any).type === "text") { if ((document.activeElement as any).type && (document.activeElement as any).type === "text") {

View File

@@ -1,64 +0,0 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
let dispatch = createEventDispatcher()
let { theme = $bindable("") }: {
theme?: string;
} = $props();
let set = (s: string) => {
theme = s
dispatch("theme_change", theme)
}
</script>
<div class="center">
<button class:button_highlight={theme===""} onclick={() => {set("")}}>
None
</button>
<button class="group_first" class:button_highlight={theme==="nord"} onclick={() => {set("nord")}}>
Nord
</button>
<button class="group_middle" class:button_highlight={theme==="nord_dark"} onclick={() => {set("nord_dark")}}>
dark
</button>
<button class="group_last" class:button_highlight={theme==="nord_light"} onclick={() => {set("nord_light")}}>
light
</button>
<button class="group_first" class:button_highlight={theme==="solarized"} onclick={() => {set("solarized")}}>
Solarized
</button>
<button class="group_middle" class:button_highlight={theme==="solarized_dark"} onclick={() => {set("solarized_dark")}}>
dark
</button>
<button class="group_last" class:button_highlight={theme==="solarized_light"} onclick={() => {set("solarized_light")}}>
light
</button>
<button class:button_highlight={theme==="purple_drain"} onclick={() => {set("purple_drain")}}>
Purple drain
</button>
<button class:button_highlight={theme==="classic"} onclick={() => {set("classic")}}>
Classic
</button>
<button class:button_highlight={theme==="maroon"} onclick={() => {set("maroon")}}>
Maroon
</button>
<button class:button_highlight={theme==="hacker"} onclick={() => {set("hacker")}}>
Hacker
</button>
<button class:button_highlight={theme==="canta"} onclick={() => {set("canta")}}>
Canta
</button>
<button class:button_highlight={theme==="skeuos"} onclick={() => {set("skeuos")}}>
Skeuos
</button>
<button class:button_highlight={theme==="sweet"} onclick={() => {set("sweet")}}>
Sweet
</button>
</div>
<style>
.center {
text-align: center;
}
</style>

View File

@@ -9,6 +9,6 @@
"target": "ES2017", "target": "ES2017",
"noUnusedLocals": true, "noUnusedLocals": true,
"module": "preserve", "module": "preserve",
"moduleResolution": "bundler", "moduleResolution": "bundler"
} }
} }