Add oembed tags. Fix sharing and copy link button. Add text and file viewer
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
<script>
|
||||
import { tick } from "svelte";
|
||||
|
||||
|
||||
let container
|
||||
let text_type = ""
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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"
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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 {
|
||||
|
@@ -98,6 +98,7 @@ td {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
vertical-align: middle;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.node_name {
|
||||
width: 100%;
|
||||
|
31
svelte/src/filesystem/viewers/File.svelte
Normal file
31
svelte/src/filesystem/viewers/File.svelte
Normal 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>
|
@@ -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>
|
||||
|
||||
|
53
svelte/src/filesystem/viewers/Text.svelte
Normal file
53
svelte/src/filesystem/viewers/Text.svelte
Normal 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>
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user