Add oembed tags. Fix sharing and copy link button. Add text and file viewer

This commit is contained in:
2023-05-17 19:27:46 +02:00
parent 277637511c
commit bd9359de44
16 changed files with 459 additions and 192 deletions

View File

@@ -1,7 +1,6 @@
<script>
import { tick } from "svelte";
let container
let text_type = ""

View File

@@ -4,12 +4,19 @@ export let navigator
</script>
{#each state.path as node, i (node.path)}
{@const shared = node.id !== undefined && node.id !== "me"}
<a
href={state.path_root+node.path}
class="breadcrumb button"
class:button_highlight={state.base_index === i}
on:click|preventDefault={() => {navigator.navigate(node.path, true)}}>
{node.name}
on:click|preventDefault={() => {navigator.navigate(node.path, true)}}
>
{#if shared}
<i class="icon small">share</i>
{/if}
<div class="node_name" class:nopad={shared}>
{node.name}
</div>
</a>
{/each}
@@ -17,8 +24,19 @@ export let navigator
.breadcrumb {
min-width: 1em;
text-align: center;
padding: 6px 8px;
margin: 4px;
word-break: break-all;
padding: 0;
display: flex;
flex-direction: row;
}
.node_name {
margin: 6px 8px;
}
.nopad {
margin-left: 0;
}
.icon {
margin: 2px 4px;
}
</style>

View File

@@ -1,11 +1,9 @@
<script>
import { fs_rename, fs_update } from "./FilesystemAPI";
import { fs_delete_all, fs_rename, fs_update } from "./FilesystemAPI";
import Modal from "../util/Modal.svelte";
import { createEventDispatcher } from "svelte";
let dispatch = createEventDispatcher()
export let bucket = ""
export let navigator
let file = {
path: "",
name: "",
@@ -14,7 +12,9 @@ let file = {
};
export let visible
export const edit = (f) => {
export const edit = (f, t = "file") => {
tab = t
console.log("Editing file", f)
file = f
@@ -26,6 +26,8 @@ export const edit = (f) => {
visible = true
}
let tab = "file"
let file_name = ""
let shared = false
let read_password = ""
@@ -33,6 +35,7 @@ let write_password = ""
let mode = ""
const save = async () => {
console.debug("Saving file", file.path)
try {
await fs_update(
bucket,
@@ -58,28 +61,83 @@ const save = async () => {
return
}
dispatch("navigate", {path: file.path, push_history: false})
navigator.navigate(file.path, false)
}
const delete_file = async () => {
try {
await fs_delete_all(bucket, file.path)
} catch (err) {
console.error(err)
alert(err)
return
}
navigator.navigate(file.path, false)
visible = false
}
</script>
<Modal bind:visible={visible} title="Edit" width="700px">
<form on:submit|preventDefault={save} style="display: flex; padding: 8px;">
<div class="form">
<label for="file_name">Name:</label>
<input bind:value={file_name} id="file_name" type="text" class="form_input"/>
<label for="file_name">Shared:</label>
<input bind:checked={shared} id="file_name" type="checkbox" class="form_input"/>
<label for="read_password">Read password:</label>
<input bind:value={read_password} id="read_password" type="text" class="form_input"/>
<label for="write_password">Write password:</label>
<input bind:value={write_password} id="write_password" type="text" class="form_input"/>
<label for="mode">Mode:</label>
<input bind:value={mode} id="mode" type="text" class="form_input"/>
<button type="submit" style="flex: 0 0 auto" class="button_highlight">
<i class="icon">save</i> Save
</button>
</div>
<Modal bind:visible={visible} title="Edit {file.name}" width="600px" on:save={save} form="file_edit_form">
<div class="tab_bar">
<button class:button_highlight={tab === "file"} on:click={() => tab = "file"}>
<i class="icon">edit</i>
Edit file
</button>
<button class:button_highlight={tab === "share"} on:click={() => tab = "share"}>
<i class="icon">share</i>
Sharing settings
</button>
</div>
<form id="file_edit_form" on:submit|preventDefault={save} style="display: flex; padding: 8px;">
{#if tab === "file"}
<div class="form">
<span class="header">File settings</span>
<label for="file_name">Name:</label>
<input bind:value={file_name} id="file_name" type="text" class="form_input"/>
<label for="mode">Mode:</label>
<input bind:value={mode} id="mode" type="text" class="form_input"/>
<span class="header">Delete</span>
<p>
Delete this file or directory. If this is a directory then all
subfiles will be deleted as well. This action cannot be undone.
</p>
<button on:click|preventDefault={delete_file} class="button_red" style="align-self: flex-start;">
<i class="icon small">delete</i> Delete
</button>
</div>
{:else if tab === "share"}
<div class="form">
<span class="header">
Sharing settings
</span>
<p>
When a file or directory is shared it can be accessed
through a unique link. You can get the URL with the 'Copy
link' button on the toolbar, or share the link with the
'Share' button. If you share a directory all the files
within the directory are also accessible from the link.
</p>
<div>
<input bind:checked={shared} id="shared" type="checkbox" class="form_input"/>
<label for="shared">Share this file or directory</label>
</div>
<label for="read_password">Read password:</label>
<input bind:value={read_password} id="read_password" type="text" class="form_input"/>
<label for="write_password">Write password:</label>
<input bind:value={write_password} id="write_password" type="text" class="form_input"/>
</div>
{/if}
</form>
</Modal>
<style>
.header {
margin: 0.5em 0;
font-size: 1.5em;
border-bottom: 1px var(--separator) solid;
}
.tab_bar {
border-bottom: 2px solid var(--separator);
}
</style>

View File

@@ -132,6 +132,8 @@ const download = () => {
toolbar_visible={toolbar_visible}
edit_window={edit_window}
on:loading={e => {state.loading = e.detail}}
on:open_sibling={e => navigator.open_sibling(e.detail)}
on:download={download}
/>
</div>
@@ -151,7 +153,7 @@ const download = () => {
bind:this={edit_window}
bind:visible={edit_visible}
bucket={state.root.id}
on:navigate={e => navigator.navigate(e.path, e.push_history)}
navigator={navigator}
/>
</div>

View File

@@ -16,7 +16,9 @@ export const fs_thumbnail_url = (bucket, path, width = 128, height = 128) => {
}
export const fs_node_type = node => {
if (node.file_type === "application/bittorrent" || node.file_type === "application/x-bittorrent") {
if (node.type === "dir") {
return "dir"
} else if (node.file_type === "application/bittorrent" || node.file_type === "application/x-bittorrent") {
return "torrent"
} else if (node.file_type === "application/zip") {
return "zip"

View File

@@ -1,6 +1,6 @@
<script>
import { fs_get_node } from "./FilesystemAPI";
import { fs_split_path } from "./FilesystemUtil";
import { fs_node_type, fs_split_path } from "./FilesystemUtil";
export let state = {
// Parts of the raw API response
@@ -69,13 +69,20 @@ export const open_node = (node, push_history) => {
node.path.forEach(cleanup_func)
node.children.forEach(cleanup_func)
// Update window title and navigation history
// Update window title and navigation history. If push_history is false we
// still replace the URL with replaceState. This way the user is not greeted
// to a 404 page when refreshing after renaming a file
window.document.title = node.path[node.base_index].name+" ~ pixeldrain"
if (push_history) {
window.history.pushState(
{}, window.document.title,
"/d/"+node.path[0].id+node.path[node.base_index].path,
)
} else {
window.history.replaceState(
{}, window.document.title,
"/d/"+node.path[0].id+node.path[node.base_index].path,
)
}
// If the new node is a child of the previous node we save the parent's
@@ -99,30 +106,7 @@ export const open_node = (node, push_history) => {
state.permissions = node.permissions
// Update the viewer area with the right viewer type
if (state.base.type === "bucket" || state.base.type === "dir") {
state.viewer_type = "dir"
} else if (state.base.file_type.startsWith("image")) {
state.viewer_type = "image"
} else if (
state.base.file_type.startsWith("audio") ||
state.base.file_type === "application/ogg" ||
state.base.name.endsWith(".mp3")
) {
state.viewer_type = "audio"
} else if (
state.base.file_type.startsWith("video") ||
state.base.file_type === "application/matroska" ||
state.base.file_type === "application/x-matroska"
) {
state.viewer_type = "video"
} else if (
state.base.file_type === "application/pdf" ||
state.base.file_type === "application/x-pdf"
) {
state.viewer_type = "pdf"
} else {
state.viewer_type = ""
}
state.viewer_type = fs_node_type(state.base)
console.debug("Opened node", node)

View File

@@ -1,62 +1,121 @@
<script context="module">
export const generate_share_url = path => {
let share_url = ""
let bucket_idx = -1
// Find the first node in the path that has a public ID
for (let i = 0; i < path.length; i++) {
if (path[i].id !== undefined && path[i].id !== "me") {
bucket_idx = i
break
}
}
if (bucket_idx !== -1) {
share_url = window.location.protocol+"//"+
window.location.host+"/d/"+
path[bucket_idx].id
// Construct the path starting from the bucket
for (let i = bucket_idx+1; i < path.length; i++) {
share_url += "/" + encodeURI(path[i].name)
}
}
return share_url
}
</script>
<script>
import { fs_update } from "./FilesystemAPI";
export let visible = false
export let share_url = ""
$: {
if (share_url === "") {
visible = false
}
}
const share = async () => {
console.debug("Making file sharable", state.base)
try {
await fs_update(state.root.id, state.base.path, {shared: true})
} catch (err) {
console.error(err)
alert(err)
return
}
navigator.navigate(state.base.path, false)
}
export let state
export let navigator
const share_email = () => {
window.open(
'mailto:please@set.address?subject=File%20on%20pixeldrain&body='+encodeURIComponent(window.location.href)
'mailto:please@set.address?subject=File%20on%20pixeldrain&body='+encodeURIComponent(share_url)
);
}
const share_reddit = () => {
window.open('https://www.reddit.com/submit?url='+encodeURIComponent(window.location.href));
window.open('https://www.reddit.com/submit?url='+encodeURIComponent(share_url));
}
const share_twitter = () => {
window.open('https://twitter.com/share?url='+encodeURIComponent(window.location.href));
window.open('https://twitter.com/share?url='+encodeURIComponent(share_url));
}
const share_facebook = () => {
window.open('http://www.facebook.com/sharer.php?u='+encodeURIComponent(window.location.href));
window.open('http://www.facebook.com/sharer.php?u='+encodeURIComponent(share_url));
}
const share_tumblr = () => {
window.open('http://www.tumblr.com/share/link?url='+encodeURIComponent(window.location.href));
window.open('http://www.tumblr.com/share/link?url='+encodeURIComponent(share_url));
}
</script>
<div class="sharebar" class:visible>
Share on:<br/>
<button class="button_full_width" on:click={share_email}>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M22 4H2v16h20V4zm-2 4l-8 5-8-5V6l8 5 8-5v2z"/>
</svg>
<br/>
E-Mail
</button>
<button class="button_full_width" on:click={share_reddit}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M22,12.14C22,10.92 21,9.96 19.81,9.96C19.22,9.96 18.68,10.19 18.29,10.57C16.79,9.5 14.72,8.79 12.43,8.7L13.43,4L16.7,4.71C16.73,5.53 17.41,6.19 18.25,6.19C19.11,6.19 19.81,5.5 19.81,4.63C19.81,3.77 19.11,3.08 18.25,3.08C17.65,3.08 17.11,3.43 16.86,3.95L13.22,3.18C13.11,3.16 13,3.18 12.93,3.24C12.84,3.29 12.79,3.38 12.77,3.5L11.66,8.72C9.33,8.79 7.23,9.5 5.71,10.58C5.32,10.21 4.78,10 4.19,10C2.97,10 2,10.96 2,12.16C2,13.06 2.54,13.81 3.29,14.15C3.25,14.37 3.24,14.58 3.24,14.81C3.24,18.18 7.16,20.93 12,20.93C16.84,20.93 20.76,18.2 20.76,14.81C20.76,14.6 20.75,14.37 20.71,14.15C21.46,13.81 22,13.04 22,12.14M7,13.7C7,12.84 7.68,12.14 8.54,12.14C9.4,12.14 10.1,12.84 10.1,13.7A1.56,1.56 0 0,1 8.54,15.26C7.68,15.28 7,14.56 7,13.7M15.71,17.84C14.63,18.92 12.59,19 12,19C11.39,19 9.35,18.9 8.29,17.84C8.13,17.68 8.13,17.43 8.29,17.27C8.45,17.11 8.7,17.11 8.86,17.27C9.54,17.95 11,18.18 12,18.18C13,18.18 14.47,17.95 15.14,17.27C15.3,17.11 15.55,17.11 15.71,17.27C15.85,17.43 15.85,17.68 15.71,17.84M15.42,15.28C14.56,15.28 13.86,14.58 13.86,13.72A1.56,1.56 0 0,1 15.42,12.16C16.28,12.16 17,12.86 17,13.72C17,14.56 16.28,15.28 15.42,15.28Z" />
</svg>
<br/>
Reddit
</button>
<button class="button_full_width" on:click={share_twitter}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" />
</svg>
<br/>
Twitter
</button>
<button class="button_full_width" on:click={share_facebook}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M18,5H15.5A3.5,3.5 0 0,0 12,8.5V11H10V14H12V21H15V14H18V11H15V9A1,1 0 0,1 16,8H18V5Z" />
</svg>
<br/>
Facebook
</button>
<button class="button_full_width" on:click={share_tumblr}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M17,11H13V15.5C13,16.44 13.28,17 14.5,17H17V21C17,21 15.54,21.05 14.17,21.05C10.8,21.05 9.5,19 9.5,16.75V11H7V7C10.07,6.74 10.27,4.5 10.5,3H13V7H17" />
</svg>
<br/>
Tumblr
</button>
{#if share_url !== ""}
Share on:<br/>
<button class="button_full_width" on:click={share_email}>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M22 4H2v16h20V4zm-2 4l-8 5-8-5V6l8 5 8-5v2z"/>
</svg>
<br/>
E-Mail
</button>
<button class="button_full_width" on:click={share_reddit}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M22,12.14C22,10.92 21,9.96 19.81,9.96C19.22,9.96 18.68,10.19 18.29,10.57C16.79,9.5 14.72,8.79 12.43,8.7L13.43,4L16.7,4.71C16.73,5.53 17.41,6.19 18.25,6.19C19.11,6.19 19.81,5.5 19.81,4.63C19.81,3.77 19.11,3.08 18.25,3.08C17.65,3.08 17.11,3.43 16.86,3.95L13.22,3.18C13.11,3.16 13,3.18 12.93,3.24C12.84,3.29 12.79,3.38 12.77,3.5L11.66,8.72C9.33,8.79 7.23,9.5 5.71,10.58C5.32,10.21 4.78,10 4.19,10C2.97,10 2,10.96 2,12.16C2,13.06 2.54,13.81 3.29,14.15C3.25,14.37 3.24,14.58 3.24,14.81C3.24,18.18 7.16,20.93 12,20.93C16.84,20.93 20.76,18.2 20.76,14.81C20.76,14.6 20.75,14.37 20.71,14.15C21.46,13.81 22,13.04 22,12.14M7,13.7C7,12.84 7.68,12.14 8.54,12.14C9.4,12.14 10.1,12.84 10.1,13.7A1.56,1.56 0 0,1 8.54,15.26C7.68,15.28 7,14.56 7,13.7M15.71,17.84C14.63,18.92 12.59,19 12,19C11.39,19 9.35,18.9 8.29,17.84C8.13,17.68 8.13,17.43 8.29,17.27C8.45,17.11 8.7,17.11 8.86,17.27C9.54,17.95 11,18.18 12,18.18C13,18.18 14.47,17.95 15.14,17.27C15.3,17.11 15.55,17.11 15.71,17.27C15.85,17.43 15.85,17.68 15.71,17.84M15.42,15.28C14.56,15.28 13.86,14.58 13.86,13.72A1.56,1.56 0 0,1 15.42,12.16C16.28,12.16 17,12.86 17,13.72C17,14.56 16.28,15.28 15.42,15.28Z" />
</svg>
<br/>
Reddit
</button>
<button class="button_full_width" on:click={share_twitter}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" />
</svg>
<br/>
Twitter
</button>
<button class="button_full_width" on:click={share_facebook}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M18,5H15.5A3.5,3.5 0 0,0 12,8.5V11H10V14H12V21H15V14H18V11H15V9A1,1 0 0,1 16,8H18V5Z" />
</svg>
<br/>
Facebook
</button>
<button class="button_full_width" on:click={share_tumblr}>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path d="M17,11H13V15.5C13,16.44 13.28,17 14.5,17H17V21C17,21 15.54,21.05 14.17,21.05C10.8,21.05 9.5,19 9.5,16.75V11H7V7C10.07,6.74 10.27,4.5 10.5,3H13V7H17" />
</svg>
<br/>
Tumblr
</button>
{:else}
This file or directory is not currently shared. Would you like to make it sharable?
<button on:click={share}>
<i class="icon">share</i>
Make sharable
</button>
{/if}
</div>
<style>

View File

@@ -1,7 +1,8 @@
<script>
import { createEventDispatcher } from "svelte";
import Sharebar from "./Sharebar.svelte";
import Sharebar, { generate_share_url } from "./Sharebar.svelte";
import { formatDataVolume, formatThousands } from "../util/Formatting.svelte";
import { copy_text } from "../util/Util.svelte";
let dispatch = createEventDispatcher()
@@ -31,6 +32,35 @@ $: {
$: total_directories = state.children.reduce((acc, cur) => cur.type === "dir" ? acc + 1 : acc, 0)
$: total_files = state.children.reduce((acc, cur) => cur.type === "file" ? acc + 1 : acc, 0)
$: total_file_size = state.children.reduce((acc, cur) => acc + cur.file_size, 0)
$: share_url = generate_share_url(state.path)
let link_copied = false
const copy_link = () => {
if (share_url === "") {
edit_window.edit(state.base, "share")
return
}
copy_text(share_url)
link_copied = true
setTimeout(() => {link_copied = false}, 60000)
}
let share = () => {
if (share_url === "") {
edit_window.edit(state.base, "share")
return
}
if (navigator.share) {
navigator.share({
title: state.base.name,
text: "I would like to share '" + state.base.name + "' with you",
url: share_url
})
} else {
sharebar_visible = !sharebar_visible
}
}
</script>
<div class="toolbar" class:toolbar_visible={visible}>
@@ -65,18 +95,25 @@ $: total_file_size = state.children.reduce((acc, cur) => acc + cur.file_size, 0)
<i class="icon">save</i> Download
</button>
{/if}
<button id="btn_download_list" class="toolbar_button" style="display: none;">
<i class="icon">save</i> DL all files
</button>
<button id="btn_copy" class="toolbar_button">
<i class="icon">content_copy</i> <u>C</u>opy Link
</button>
<button on:click={() => sharebar_visible = !sharebar_visible} class="toolbar_button" class:button_highlight={sharebar_visible}>
<i class="icon">share</i> Share
</button>
{#if state.base.path !== "/"}
<button id="btn_copy" class="toolbar_button" on:click={copy_link} class:button_highlight={link_copied}>
<i class="icon">content_copy</i> <u>C</u>opy Link
</button>
<button on:click={share} class="toolbar_button" class:button_highlight={sharebar_visible}>
<i class="icon">share</i> Share
</button>
{/if}
<button on:click={() => details_visible = !details_visible} class="toolbar_button" class:button_highlight={details_visible}>
<i class="icon">help</i> Deta<u>i</u>ls
</button>
{#if state.base.path !== "/"}
<button on:click={() => edit_window.edit(state.base)} class="toolbar_button" class:button_highlight={edit_visible}>
<i class="icon">edit</i> <u>E</u>dit
@@ -84,7 +121,7 @@ $: total_file_size = state.children.reduce((acc, cur) => acc + cur.file_size, 0)
{/if}
</div>
<Sharebar visible={sharebar_visible}/>
<Sharebar visible={sharebar_visible} state={state} navigator={navigator} share_url={share_url}/>
<style>
.toolbar {

View File

@@ -98,6 +98,7 @@ td {
height: 32px;
width: 32px;
vertical-align: middle;
border-radius: 4px;
}
.node_name {
width: 100%;

View File

@@ -0,0 +1,31 @@
<script>
import { createEventDispatcher } from "svelte";
import IconBlock from "../../file_viewer/viewers/IconBlock.svelte";
import { fs_thumbnail_url } from "../FilesystemUtil";
let dispatch = createEventDispatcher()
export let state
</script>
<h1>{state.base.name}</h1>
<IconBlock icon_href={fs_thumbnail_url(state.root.id, state.base.path)}>
Type: {state.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")}}>
<i class="icon">download</i>
<span>Download</span>
</button>
</IconBlock>
<style>
h1 {
text-shadow: 1px 1px 3px var(--shadow_color);
line-break: anywhere;
}
.icon {
display: inline-block;
vertical-align: middle;
}
</style>

View File

@@ -1,8 +1,10 @@
<script>
import FileManager from "../filemanager/FileManager.svelte";
import Audio from "./Audio.svelte";
import File from "./File.svelte";
import Image from "./Image.svelte";
import Pdf from "./PDF.svelte";
import Text from "./Text.svelte";
import Video from "./Video.svelte";
export let navigator
@@ -20,13 +22,17 @@ export let edit_window
on:loading
/>
{:else if state.viewer_type === "audio"}
<Audio state={state} on:open_sibling={e => {navigator.open_sibling(e.detail)}}/>
<Audio state={state} on:open_sibling/>
{:else if state.viewer_type === "image"}
<Image state={state} on:open_sibling={e => {navigator.open_sibling(e.detail)}}/>
<Image state={state} on:open_sibling/>
{:else if state.viewer_type === "video"}
<Video state={state} on:open_sibling={e => {navigator.open_sibling(e.detail)}}/>
<Video state={state} on:open_sibling/>
{:else if state.viewer_type === "pdf"}
<Pdf state={state}/>
{:else if state.viewer_type === "text"}
<Text state={state}/>
{:else}
<File state={state}/>
{/if}
</div>

View File

@@ -0,0 +1,53 @@
<script>
import { fs_file_url } from "../FilesystemUtil";
export let state
let text_pre
$: set_file(state.base)
export const set_file = file => {
console.debug("Loading text file", file.name)
if (file.size > 1 << 22) { // File larger than 4 MiB
text_pre.innerText = "File is too large to view online.\nPlease download and view it locally."
return
}
fetch(fs_file_url(state.root.id, file.path)).then(resp => {
if (!resp.ok) { return Promise.reject(resp.status) }
return resp.text()
}).then(resp => {
text_pre.innerText = resp
}).catch(err => {
text_pre.innerText = "Error loading file: " + err
})
}
</script>
<div class="container">
<pre bind:this={text_pre}>
Loading...
</pre>
</div>
<style>
.container {
background: var(--body_color);
text-align: left;
height: 100%;
width: 100%;
line-height: 1.5em;
overflow-y: auto;
overflow-x: hidden;
}
.container > pre {
margin: 0;
padding: 10px;
white-space: pre-wrap;
overflow: hidden;
border: none;
font-size: 0.9em;
}
</style>

View File

@@ -3,10 +3,15 @@
// incremented every time a modal is shown
let global_index = 10000;
</script>
<script>
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
// Form can be used to turn the modal into a save dialog. Enter the ID of a form
// inside the modal and the modal will provide a submit button for that form
export let form = ""
export let title = "";
export let width = "800px";
export let height = "auto";
@@ -50,13 +55,36 @@ const keydown = e => {
{#if visible}
<div class="background" use:load_bg on:click={hide} transition:fade={{duration: 200}} on:keydown={keydown}>
<div class="top_padding"></div>
<div class="window" use:load_modal on:click|stopPropagation role="dialog" aria-modal="true" on:keydown={keydown}>
<div
class="window"
class:small_radius={form !== ""}
use:load_modal
on:click|stopPropagation
role="dialog"
aria-modal="true"
on:keydown={keydown}
>
<div class="header">
<slot name="title">
<div class="title">{title}</div>
<button class="button round" on:click={hide}>
<i class="icon">close</i>
</button>
{#if form !== ""}
<button class="button" on:click={hide}>
<i class="icon">close</i> Cancel
</button>
<div class="title">{title}</div>
<button
class="button button_highlight"
type="submit"
form="{form}"
on:click={() => {dispatch("save"); hide()}}
>
<i class="icon">save</i> Save
</button>
{:else}
<div class="title">{title}</div>
<button class="button round" on:click={hide}>
<i class="icon">close</i>
</button>
{/if}
</slot>
</div>
<div class="body" class:padding>
@@ -69,7 +97,7 @@ const keydown = e => {
<style>
.background {
position: fixed;
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
@@ -95,10 +123,13 @@ these padding divs to move it 25% up */
max-height: 100%;
max-width: 100%;
padding: 0;
border-radius: 20px 20px 8px 8px;
border-radius: 16px 16px 8px 8px;
overflow: hidden;
text-align: left;
}
.small_radius {
border-radius: 8px;
}
.header {
flex-grow: 0;
flex-shrink: 0;