Change filesystem navigator into a class with a svelte store implementation

This commit is contained in:
2024-08-09 13:02:07 +02:00
parent c481a89051
commit 69744e41a6
28 changed files with 534 additions and 530 deletions

View File

@@ -4,8 +4,7 @@ import { fs_encode_path, fs_node_icon, fs_path_url } from '../FilesystemUtil';
import FileTitle from '../../file_viewer/viewers/FileTitle.svelte';
import TextBlock from '../../file_viewer/viewers/TextBlock.svelte';
export let fs_navigator
export let state
export let nav
let player
let playing = false
let media_session = false
@@ -18,13 +17,13 @@ export const toggle_playback = () => {
export const update = async () => {
if (media_session) {
navigator.mediaSession.metadata = new MediaMetadata({
title: state.base.name,
title: nav.base.name,
artist: "pixeldrain",
album: "unknown",
});
}
siblings = await fs_navigator.get_siblings()
siblings = await nav.get_siblings()
}
onMount(() => {
@@ -33,30 +32,30 @@ onMount(() => {
navigator.mediaSession.setActionHandler('play', () => player.play());
navigator.mediaSession.setActionHandler('pause', () => player.pause());
navigator.mediaSession.setActionHandler('stop', () => player.stop());
navigator.mediaSession.setActionHandler('previoustrack', () => fs_navigator.open_sibling(-1));
navigator.mediaSession.setActionHandler('nexttrack', () => fs_navigator.open_sibling(1));
navigator.mediaSession.setActionHandler('previoustrack', () => nav.open_sibling(-1));
navigator.mediaSession.setActionHandler('nexttrack', () => nav.open_sibling(1));
}
})
</script>
<slot></slot>
<FileTitle title={state.base.name}/>
<FileTitle title={$nav.base.name}/>
<TextBlock width="1000px">
<audio
bind:this={player}
class="player"
src={fs_path_url(state.base.path)}
src={fs_path_url($nav.base.path)}
autoplay="autoplay"
controls="controls"
on:pause={() => playing = false }
on:play={() => playing = true }
on:ended={() => fs_navigator.open_sibling(1) }>
on:ended={() => nav.open_sibling(1) }>
<track kind="captions"/>
</audio>
<div style="text-align: center;">
<button on:click={() => fs_navigator.open_sibling(-1) }><i class="icon">skip_previous</i></button>
<button on:click={() => nav.open_sibling(-1) }><i class="icon">skip_previous</i></button>
<button on:click={() => player.currentTime -= 10 }><i class="icon">replay_10</i></button>
<button on:click={toggle_playback}>
{#if playing}
@@ -66,17 +65,17 @@ onMount(() => {
{/if}
</button>
<button on:click={() => player.currentTime += 10 }><i class="icon">forward_10</i></button>
<button on:click={() => fs_navigator.open_sibling(1) }><i class="icon">skip_next</i></button>
<button on:click={() => nav.open_sibling(1) }><i class="icon">skip_next</i></button>
</div>
<h2>Tracklist</h2>
{#each siblings as sibling (sibling.path)}
<a
href={"/d"+fs_encode_path(sibling.path)}
on:click|preventDefault={() => fs_navigator.navigate(sibling.path, true)}
on:click|preventDefault={() => nav.navigate(sibling.path, true)}
class="node"
>
{#if sibling.path === state.base.path}
{#if sibling.path === $nav.base.path}
<i class="play_arrow icon">play_arrow</i>
{:else}
<img src={fs_node_icon(sibling, 64, 64)} class="node_icon" alt="icon"/>

View File

@@ -5,15 +5,15 @@ import { fs_thumbnail_url } from "../FilesystemUtil";
import TextBlock from "../../file_viewer/viewers/TextBlock.svelte";
let dispatch = createEventDispatcher()
export let state
export let nav
</script>
<slot></slot>
<h1>{state.base.name}</h1>
<h1>{$nav.base.name}</h1>
<IconBlock icon_href={fs_thumbnail_url(state.base.path, 256, 256)}>
Type: {state.base.file_type}<br/>
<IconBlock icon_href={fs_thumbnail_url($nav.base.path, 256, 256)}>
Type: {$nav.base.file_type}<br/>
No preview is available for this file type. Download to view it locally.
<br/>
<button class="button_highlight" on:click={() => {dispatch("download")}}>
@@ -22,7 +22,7 @@ export let state
</button>
</IconBlock>
{#if state.base.path === "/me/.search_index.gz"}
{#if $nav.base.path === "/me/.search_index.gz"}
<TextBlock>
<p>
Congratulations! You have found the search index. One of the

View File

@@ -1,5 +1,5 @@
<script>
import { tick } from "svelte";
import { onMount, tick } from "svelte";
import Spinner from "../../util/Spinner.svelte";
import { fs_node_type } from "../FilesystemUtil";
import FileManager from "../filemanager/FileManager.svelte";
@@ -13,25 +13,25 @@ import Torrent from "./Torrent.svelte";
import Zip from "./Zip.svelte";
import CustomBanner from "./CustomBanner.svelte";
export let fs_navigator
export let nav
export let edit_window
export let state
let viewer
let viewer_type = ""
let last_path = ""
$: state_update(state.base)
const state_update = async (base) => {
if (base.path === last_path) {
onMount(() => nav.subscribe(state_update))
const state_update = async () => {
if (!nav.initialized || nav.base.path === last_path) {
return
}
last_path = base.path
last_path = nav.base.path
// Update the viewer area with the right viewer type
viewer_type = fs_node_type(base)
viewer_type = fs_node_type(nav.base)
console.debug("Previewing file", base, "viewer type", viewer_type)
console.debug("Previewing file", nav.base, "viewer type", viewer_type)
// Render the viewer component and set the file type
await tick()
@@ -52,40 +52,34 @@ export const toggle_playback = () => {
<Spinner></Spinner>
</div>
{:else if viewer_type === "dir"}
<FileManager
fs_navigator={fs_navigator}
state={state}
edit_window={edit_window}
on:loading
on:upload_picker
>
<CustomBanner path={state.path}/>
<FileManager nav={nav} edit_window={edit_window} on:loading on:upload_picker>
<CustomBanner path={$nav.path}/>
</FileManager>
{:else if viewer_type === "audio"}
<Audio state={state} bind:this={viewer} fs_navigator={fs_navigator}>
<CustomBanner path={state.path}/>
<Audio nav={nav} bind:this={viewer}>
<CustomBanner path={$nav.path}/>
</Audio>
{:else if viewer_type === "image"}
<Image state={state} bind:this={viewer} on:open_sibling/>
<Image nav={nav} bind:this={viewer}/>
{:else if viewer_type === "video"}
<Video state={state} bind:this={viewer} on:open_sibling/>
<Video nav={nav} bind:this={viewer} on:open_sibling/>
{:else if viewer_type === "pdf"}
<Pdf state={state}/>
<Pdf nav={nav}/>
{:else if viewer_type === "text"}
<Text state={state} bind:this={viewer}>
<CustomBanner path={state.path}/>
<Text nav={nav} bind:this={viewer}>
<CustomBanner path={$nav.path}/>
</Text>
{:else if viewer_type === "torrent"}
<Torrent state={state} bind:this={viewer} on:loading on:download>
<CustomBanner path={state.path}/>
<Torrent nav={nav} bind:this={viewer} on:loading on:download>
<CustomBanner path={$nav.path}/>
</Torrent>
{:else if viewer_type === "zip"}
<Zip state={state} bind:this={viewer} on:loading on:download>
<CustomBanner path={state.path}/>
<Zip nav={nav} bind:this={viewer} on:loading on:download>
<CustomBanner path={$nav.path}/>
</Zip>
{:else}
<File state={state} on:download>
<CustomBanner path={state.path}/>
<File nav={nav} on:download>
<CustomBanner path={$nav.path}/>
</File>
{/if}

View File

@@ -5,7 +5,7 @@ import { fs_path_url } from "../FilesystemUtil";
let dispatch = createEventDispatcher()
export let state
export let nav
let container
let zoom = false
let x, y = 0
@@ -64,8 +64,8 @@ const mouseup = (e) => {
class:zoom
use:swipe_nav={!zoom}
on:style={e => swipe_style = e.detail}
on:prev={() => dispatch("open_sibling", -1)}
on:next={() => dispatch("open_sibling", 1)}
on:prev={() => nav.open_sibling(-1)}
on:next={() => nav.open_sibling(1)}
>
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<img
@@ -77,7 +77,7 @@ const mouseup = (e) => {
class="image"
class:zoom
style={swipe_style}
src={fs_path_url(state.base.path)}
src={fs_path_url($nav.base.path)}
alt="no description available" />
</div>

View File

@@ -1,12 +1,12 @@
<script>
import { fs_path_url } from "../FilesystemUtil";
export let state
export let nav
</script>
<iframe
class="container"
src={"/res/misc/pdf-viewer/web/viewer.html?file="+encodeURIComponent(fs_path_url(state.base.path))}
src={"/res/misc/pdf-viewer/web/viewer.html?file="+encodeURIComponent(fs_path_url($nav.base.path))}
title="PDF viewer">
</iframe>

View File

@@ -2,26 +2,25 @@
import { tick } from "svelte";
import { fs_path_url } from "../FilesystemUtil";
export let state
export let nav
let text_type = "text"
export const update = () => {
const file = state.base
console.debug("Loading text file", file.name)
console.debug("Loading text file", nav.base.name)
if (file.size > 1 << 21) { // File larger than 2 MiB
if (nav.base.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 (
file.file_type.startsWith("text/markdown") ||
file.name.endsWith(".md") ||
file.name.endsWith(".markdown")
nav.base.file_type.startsWith("text/markdown") ||
nav.base.name.endsWith(".md") ||
nav.base.name.endsWith(".markdown")
) {
markdown(file)
markdown(nav.base)
} else {
text(file)
text(nav.base)
}
}

View File

@@ -10,7 +10,7 @@ import CopyButton from "../../layout/CopyButton.svelte";
let dispatch = createEventDispatcher()
export let state
export let nav
let status = "loading"
@@ -18,7 +18,7 @@ export const update = async () => {
dispatch("loading", true)
try {
let resp = await fetch(fs_path_url(state.base.path)+"?torrent_info")
let resp = await fetch(fs_path_url(nav.base.path)+"?torrent_info")
if (resp.status >= 400) {
let json = await resp.json()
@@ -63,9 +63,9 @@ let magnet = ""
<slot></slot>
<h1>{state.base.name}</h1>
<h1>{$nav.base.name}</h1>
<IconBlock icon_href={fs_node_icon(state.base, 256, 256)}>
<IconBlock icon_href={fs_node_icon($nav.base, 256, 256)}>
{#if status === "finished"}
Created by: {torrent.created_by}<br/>
Comment: {torrent.comment}<br/>

View File

@@ -3,7 +3,7 @@ import { onMount, createEventDispatcher, tick } from "svelte";
import { fs_path_url } from "../FilesystemUtil";
let dispatch = createEventDispatcher()
export let state
export let nav
// Used to detect when the file path changes
let last_path = ""
@@ -17,20 +17,20 @@ let loop = false
export const update = async () => {
if (media_session) {
navigator.mediaSession.metadata = new MediaMetadata({
title: state.base.name,
title: nav.base.name,
artist: "pixeldrain",
album: "unknown",
});
console.debug("Updating media session")
}
loop = state.base.name.includes(".loop.")
loop = nav.base.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 (state.base.path != last_path) {
last_path = state.base.path
if (nav.base.path != last_path) {
last_path = nav.base.path
loaded = false
await tick()
loaded = true
@@ -75,9 +75,9 @@ const fullscreen = () => {
<div class="container">
{#if
state.base.file_type === "video/x-matroska" ||
state.base.file_type === "video/quicktime" ||
state.base.file_type === "video/x-ms-asf"
$nav.base.file_type === "video/x-matroska" ||
$nav.base.file_type === "video/quicktime" ||
$nav.base.file_type === "video/x-ms-asf"
}
<div class="compatibility_warning">
This video file type is not compatible with every web
@@ -100,7 +100,7 @@ const fullscreen = () => {
on:play={() => playing = true }
on:ended={() => dispatch("open_sibling", 1)}
>
<source src={fs_path_url(state.base.path)} type={state.base.file_type} />
<source src={fs_path_url($nav.base.path)} type={$nav.base.file_type} />
</video>
{/if}
</div>

View File

@@ -8,7 +8,7 @@ import { fs_node_icon, fs_path_url } from "../FilesystemUtil";
let dispatch = createEventDispatcher()
export let state
export let nav
let status = "loading"
@@ -23,14 +23,14 @@ let archive_type = ""
export const update = async () => {
dispatch("loading", true)
if (state.base.file_type === "application/zip") {
if (nav.base.file_type === "application/zip") {
archive_type = "zip"
} else if (state.base.file_type === "application/x-7z-compressed") {
} else if (nav.base.file_type === "application/x-7z-compressed") {
archive_type = "7z"
}
try {
let resp = await fetch(fs_path_url(state.base.path)+"?zip_info")
let resp = await fetch(fs_path_url(nav.base.path)+"?zip_info")
if (resp.status >= 400) {
status = "parse_failed"
@@ -43,11 +43,11 @@ export const update = async () => {
// downloaded. If so then we set the download URL for each file
if (zip.properties && zip.properties.includes("read_individual_files")) {
// Set the download URL for each file in the zip
recursive_set_url(fs_path_url(state.base.path)+"?zip_file=", zip)
recursive_set_url(fs_path_url(nav.base.path)+"?zip_file=", zip)
}
uncomp_size = recursive_size(zip)
comp_ratio = (uncomp_size / state.base.file_size)
comp_ratio = (uncomp_size / nav.base.file_size)
} catch (err) {
console.error(err)
} finally {
@@ -85,18 +85,18 @@ const recursive_size = (file) => {
<slot></slot>
<h1>{state.base.name}</h1>
<h1>{$nav.base.name}</h1>
<IconBlock icon_href={fs_node_icon(state.base, 256, 256)}>
<IconBlock icon_href={fs_node_icon($nav.base, 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(state.base.file_size, 3)}<br/>
Compressed size: {formatDataVolume($nav.base.file_size, 3)}<br/>
Uncompressed size: {formatDataVolume(zip.size, 3)} (Ratio: {comp_ratio.toFixed(2)}x)<br/>
Uploaded on: {formatDate(state.base.created, true, true, true)}
Uploaded on: {formatDate($nav.base.created, true, true, true)}
<br/>
<button class="button_highlight" on:click={() => {dispatch("download")}}>
<i class="icon">download</i>