* Breadcrumbs too wide in deeply nested directories
* Sharedialog copies link and calls navigator.share at the same time
* Link copy feedback is not clear
* Show embed code when public read
* Hide hidden files from audio playlist
This commit is contained in:
2026-08-05 21:54:49 +02:00
parent 00b40ab900
commit 4c57a363b2
6 changed files with 57 additions and 55 deletions

View File

@@ -36,7 +36,7 @@ onMount(() => {
{:else if node.is_shared()} {:else if node.is_shared()}
<i class="icon small">share</i> <i class="icon small">share</i>
{/if} {/if}
<div class="node_name" class:base={$nav.base_index === i}> <div class="node_name" class:base={$nav.base_index === i} class:small={$nav.path.length > 10}>
{node.name} {node.name}
</div> </div>
</a> </a>
@@ -64,6 +64,9 @@ onMount(() => {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.small {
max-width: 10vw;
}
.base { .base {
/* The base name uses all available space */ /* The base name uses all available space */
max-width: unset; max-width: unset;

View File

@@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { FSNavigator } from "./FSNavigator"; import type { FSNavigator } from "./FSNavigator";
import { fs_check_is_error, fs_node_icon, fs_share_hotlink_url, fs_share_url, fs_update, type FSNode, type FSPermissions } from "lib/FilesystemAPI.svelte"; import { fs_check_is_error, fs_node_icon, fs_share_hotlink_url, fs_share_url, fs_update, type FSNode, type FSPermissions } from "lib/FilesystemAPI.svelte";
import { copy_text } from "util/Util";
import CopyButton from "layout/CopyButton.svelte"; import CopyButton from "layout/CopyButton.svelte";
import Dialog from "layout/Dialog.svelte"; import Dialog from "layout/Dialog.svelte";
import { fade } from "svelte/transition"; import { fade } from "svelte/transition";
import { get_user } from "lib/NovaAPI"; import { get_user } from "lib/NovaAPI";
import Button from "layout/Button.svelte";
let { nav }: { let { nav }: {
nav: FSNavigator; nav: FSNavigator;
@@ -13,10 +13,8 @@ let { nav }: {
let path: FSNode[] let path: FSNode[]
let base: FSNode = $state() let base: FSNode = $state()
let toast = $state("")
let share_url = $state("") let share_url = $state("")
let direct_share_url = $state("") let direct_share_url = $state("")
let is_parent = $state(false)
let parent_node: FSNode = $state() let parent_node: FSNode = $state()
let dialog: Dialog = $state() let dialog: Dialog = $state()
@@ -71,15 +69,19 @@ const make_public = async () => {
} }
} }
let share_parent = $state(false)
let navigator_share = $state(false)
const share = async () => { const share = async () => {
// If the base node does not have a public ID then the file is shared // If the base node is not shared then the file is shared through a parent
// through a parent node. In that case we ask the user if it was their // node. In that case we ask the user if it was their intention to share the
// intention to share the parent directory // parent directory
is_parent = base.id === undefined share_parent = !base.is_shared()
if (is_parent) { navigator_share = navigator.share !== undefined
if (!base.is_shared()) {
// Walk path backwards looking for the last public ID // Walk path backwards looking for the last public ID
for (let i = path.length - 1; i >= 0; i--) { for (let i = path.length - 1; i >= 0; i--) {
if (path[i].id !== undefined && path[i].id !== "me") { if (path[i].is_shared()) {
parent_node = path[i] parent_node = path[i]
break break
} }
@@ -88,25 +90,21 @@ const share = async () => {
share_url = fs_share_url(path) share_url = fs_share_url(path)
direct_share_url = fs_share_hotlink_url(path) direct_share_url = fs_share_hotlink_url(path)
}
const nav_share = async (url: string) => {
try { try {
await navigator.share({ await navigator.share({
title: base.name, title: base.name,
text: "I would like to share '" + base.name + "' with you", text: "I would like to share '" + base.name + "' with you",
url: share_url, url: url,
}) })
} catch(_) { } catch(err) {
if (copy_text(share_url)) { alert("navigator share not available: "+ err)
toast = "Link copied to clipboard"
setTimeout(() => {toast = ""}, 10000)
} else {
alert("Could not copy text")
}
} }
} }
</script> </script>
<Dialog bind:this={dialog_not_allowed}> <Dialog bind:this={dialog_not_allowed}>
<div class="dialog_inner"> <div class="dialog_inner">
<div class="highlight_yellow" transition:fade> <div class="highlight_yellow" transition:fade>
@@ -118,15 +116,13 @@ const share = async () => {
<Dialog bind:this={dialog}> <Dialog bind:this={dialog}>
<div class="dialog_inner"> <div class="dialog_inner">
{#if toast !== ""}
<div class="highlight_green" transition:fade>{toast}</div>
<div class="separator" transition:fade></div>
{/if}
<div>Sharing link</div> <div>Sharing link</div>
<div class="link_copy"> <div class="link_copy">
<div class="button_container"> <div class="button_container">
<CopyButton text={share_url}>Copy</CopyButton> <CopyButton text={share_url}>Copy</CopyButton>
{#if navigator_share}
<Button icon="share" label="Share" click={() => {nav_share(share_url)}}/>
{/if}
</div> </div>
<a href="{share_url}">{share_url}</a> <a href="{share_url}">{share_url}</a>
</div> </div>
@@ -135,11 +131,14 @@ const share = async () => {
<div class="link_copy"> <div class="link_copy">
<div class="button_container"> <div class="button_container">
<CopyButton text={direct_share_url}>Copy</CopyButton> <CopyButton text={direct_share_url}>Copy</CopyButton>
{#if navigator_share}
<Button icon="share" label="Share" click={() => {nav_share(direct_share_url)}}/>
{/if}
</div> </div>
<a href="{direct_share_url}">{direct_share_url}</a> <a href="{direct_share_url}">{direct_share_url}</a>
</div> </div>
{#if is_parent} {#if share_parent}
<div class="separator"></div> <div class="separator"></div>
<div> <div>
This link also gives access to This link also gives access to

View File

@@ -40,7 +40,6 @@ export const copy_link = () => {
<FileStats nav={nav} bind:expanded={toolbar_expanded}/> <FileStats nav={nav} bind:expanded={toolbar_expanded}/>
<div class="grid" class:hide={!toolbar_expanded}> <div class="grid" class:hide={!toolbar_expanded}>
<div class="button_row"> <div class="button_row">
<button onclick={() => {nav.open_sibling(-1)}}> <button onclick={() => {nav.open_sibling(-1)}}>
<i class="icon">skip_previous</i> <i class="icon">skip_previous</i>
@@ -73,7 +72,11 @@ export const copy_link = () => {
{#if path_is_shared($nav.path)} {#if path_is_shared($nav.path)}
<button onclick={copy_link} class:button_highlight={link_copied}> <button onclick={copy_link} class:button_highlight={link_copied}>
<i class="icon">content_copy</i> <i class="icon">content_copy</i>
<span><u>C</u>opy link</span> {#if link_copied}
<span>Link copied!</span>
{:else}
<span><u>C</u>opy link</span>
{/if}
</button> </button>
{/if} {/if}

View File

@@ -4,7 +4,6 @@ import CopyButton from "layout/CopyButton.svelte";
import { formatDate } from "util/Formatting"; import { formatDate } from "util/Formatting";
import { type FSNode, type NodeOptions } from "lib/FilesystemAPI.svelte"; import { type FSNode, type NodeOptions } from "lib/FilesystemAPI.svelte";
import AccessControl from "./AccessControl.svelte"; import AccessControl from "./AccessControl.svelte";
import { onMount } from 'svelte';
import { user } from "lib/UserStore"; import { user } from "lib/UserStore";
let { let {
@@ -15,27 +14,24 @@ let {
options: NodeOptions; options: NodeOptions;
} = $props(); } = $props();
let embed_html: string = $state() let is_shared = $derived(file.is_shared() || options.link_permissions?.read)
let preview_area: HTMLDivElement = $state() let embed_html: string = $derived.by(() => {
if (!is_shared) {
const embed_iframe = (file: FSNode, options: NodeOptions) => { return "File is not shared, can't generate embed code"
if (!file.is_shared()) {
example = false
embed_html = "File is not shared, can't generate embed code"
return
} }
let url = domain_url()+"/d/"+file.id let url = domain_url()+"/d/"+file.id
embed_html = `<iframe ` + return `<iframe ` +
`src="${url}" ` + `src="${url}" ` +
`style="border: none; width: 100%; max-width 90vw; height: 800px; max-height: 75vh; border-radius: 6px;" ` + `style="border: none; width: 100%; max-width 90vw; height: 800px; max-height: 75vh; border-radius: 6px;" ` +
`allowfullscreen` + `allowfullscreen` +
`></iframe>` `></iframe>`
} })
let preview_area: HTMLDivElement = $state()
let example = $state(false) let example = $state(false)
const toggle_example = () => { const toggle_example = () => {
if (file.is_shared()) { if (is_shared) {
example = !example example = !example
if (example) { if (example) {
preview_area.innerHTML = embed_html preview_area.innerHTML = embed_html
@@ -46,9 +42,6 @@ const toggle_example = () => {
} }
let share_link = $derived(window.location.protocol+"//"+window.location.host+"/d/"+file.id) let share_link = $derived(window.location.protocol+"//"+window.location.host+"/d/"+file.id)
onMount(() => {
embed_iframe(file, options)
});
</script> </script>
{#if $user.subscription.file_sharing === false} {#if $user.subscription.file_sharing === false}
@@ -93,8 +86,8 @@ onMount(() => {
<div class="center"> <div class="center">
<textarea bind:value={embed_html} style="width: 100%; height: 4em;"></textarea> <textarea bind:value={embed_html} style="width: 100%; height: 4em;"></textarea>
<br/> <br/>
<CopyButton text={embed_html}>Copy HTML</CopyButton> <CopyButton text={embed_html} disabled={!is_shared}>Copy HTML</CopyButton>
<button onclick={toggle_example} class:button_highlight={example} disabled={!file.is_shared()}> <button onclick={toggle_example} class:button_highlight={example} disabled={!is_shared}>
<i class="icon">visibility</i> Show example <i class="icon">visibility</i> Show example
</button> </button>
</div> </div>

View File

@@ -91,16 +91,18 @@ onMount(() => {
</div> </div>
<h2>Tracklist</h2> <h2>Tracklist</h2>
{#each siblings as sibling (sibling.path)} {#each siblings as sibling (sibling.id)}
<a href={"/d"+fs_encode_path(sibling.path)} class="node" class:playing={sibling.path === $nav.base.path}> {#if !sibling.is_hidden()}
{#if sibling.path === $nav.base.path} <a href={"/d"+fs_encode_path(sibling.path)} class="node" class:playing={sibling.path === $nav.base.path}>
<i class="play_arrow icon">play_arrow</i> {#if sibling.path === $nav.base.path}
{:else} <i class="play_arrow icon">play_arrow</i>
<img src={fs_node_icon(sibling, 64, 64)} class="node_icon" alt="icon"/> {:else}
{/if} <img src={fs_node_icon(sibling, 64, 64)} class="node_icon" alt="icon"/>
<span>{sibling.name}</span> {/if}
<br/> <span>{sibling.name}</span>
</a> <br/>
</a>
{/if}
{/each} {/each}
</TextBlock> </TextBlock>
</div> </div>

View File

@@ -6,6 +6,7 @@ let {
style = "", style = "",
large_icon = false, large_icon = false,
small_icon = false, small_icon = false,
disabled = false,
children, children,
onclick, onclick,
}: { }: {
@@ -13,6 +14,7 @@ let {
style?: string; style?: string;
large_icon?: boolean; large_icon?: boolean;
small_icon?: boolean; small_icon?: boolean;
disabled?: boolean;
children?: import('svelte').Snippet; children?: import('svelte').Snippet;
onclick?: (e: MouseEvent) => void; onclick?: (e: MouseEvent) => void;
} = $props(); } = $props();
@@ -50,7 +52,7 @@ export const copy = (e: MouseEvent) => {
class:large_icon class:large_icon
class:small_icon class:small_icon
title="Copy text to clipboard" title="Copy text to clipboard"
disabled={text === ""} disabled={disabled}
> >
<i class="icon">content_copy</i> <i class="icon">content_copy</i>
<span> <span>