Allow drag-and-drop uploading in album viewer
This commit is contained in:
@@ -21,6 +21,7 @@ import CustomBanner from "./CustomBanner.svelte";
|
||||
import LoadingIndicator from "../util/LoadingIndicator.svelte";
|
||||
import TransferLimit from "./TransferLimit.svelte";
|
||||
import ListStats from "./ListStats.svelte";
|
||||
import ListUpdater from "./ListUpdater.svelte";
|
||||
|
||||
let loading = true
|
||||
let embedded = false
|
||||
@@ -73,6 +74,7 @@ let toolbar_toggle = () => {
|
||||
}
|
||||
|
||||
let downloader
|
||||
let list_updater
|
||||
let details_window
|
||||
let details_visible = false
|
||||
let qr_window
|
||||
@@ -354,6 +356,10 @@ const keyboard_event = evt => {
|
||||
case "q": // Q to close the window
|
||||
window.close()
|
||||
break
|
||||
case "u": // U to upload new files
|
||||
if (list_updater) {
|
||||
list_updater.pick_files()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,22 +567,20 @@ const keyboard_event = evt => {
|
||||
on:prev={() => { if (list_navigator) { list_navigator.prev() }}}
|
||||
on:next={() => { if (list_navigator) { list_navigator.next() }}}
|
||||
on:loading={e => {loading = e.detail}}
|
||||
on:reload={reload}>
|
||||
</FilePreview>
|
||||
on:reload={reload}
|
||||
/>
|
||||
{:else if view === "gallery"}
|
||||
<GalleryView
|
||||
list={list}
|
||||
on:reload={reload}
|
||||
on:loading={e => {loading = e.detail}}>
|
||||
</GalleryView>
|
||||
on:update_list={e => list_updater.update(e.detail)}
|
||||
on:pick_files={() => list_updater.pick_files()}
|
||||
on:upload_files={e => list_updater.upload_files(e.detail)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Sharebar bind:this={sharebar}></Sharebar>
|
||||
|
||||
<!-- {#if ads_enabled}
|
||||
<AdSkyscraper on:visibility={e => {skyscraper_visible = e.detail}}></AdSkyscraper>
|
||||
{/if} -->
|
||||
</div>
|
||||
|
||||
{#if ads_enabled}
|
||||
@@ -611,6 +615,15 @@ const keyboard_event = evt => {
|
||||
{/if}
|
||||
|
||||
<Downloader bind:this={downloader} file={file} list={list}></Downloader>
|
||||
|
||||
{#if is_list && list.can_edit}
|
||||
<ListUpdater
|
||||
bind:this={list_updater}
|
||||
list={list}
|
||||
on:reload={reload}
|
||||
on:loading={e => {loading = e.detail}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
@@ -6,74 +6,28 @@ import { file_type } from "./FileUtilities.svelte";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let list = {
|
||||
id: "",
|
||||
title: "",
|
||||
files: [],
|
||||
download_href: "",
|
||||
info_href: "",
|
||||
can_edit: false,
|
||||
}
|
||||
|
||||
let file_picker;
|
||||
|
||||
const update_list = async new_files => {
|
||||
dispatch("loading", true)
|
||||
|
||||
// If the list is empty we simply delete it
|
||||
if (list.files.length === 0) {
|
||||
try {
|
||||
let resp = await fetch(list.info_href, {method: "DELETE"})
|
||||
if (resp.status >= 400) {
|
||||
throw (await resp.json()).message
|
||||
}
|
||||
window.close()
|
||||
} catch (err) {
|
||||
alert("Failed to delete album: "+err)
|
||||
} finally {
|
||||
dispatch("loading", false)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let listjson = {
|
||||
title: list.title,
|
||||
files: [],
|
||||
}
|
||||
list.files.forEach(f => {
|
||||
listjson.files.push({
|
||||
id: f.id,
|
||||
})
|
||||
})
|
||||
|
||||
try {
|
||||
const resp = await fetch(
|
||||
list.info_href,
|
||||
{ method: "PUT", body: JSON.stringify(listjson) },
|
||||
);
|
||||
if (resp.status >= 400) {
|
||||
throw (await resp.json()).message
|
||||
}
|
||||
} catch (err) {
|
||||
alert("Failed to update album: "+err)
|
||||
} finally {
|
||||
dispatch("loading", false)
|
||||
}
|
||||
}
|
||||
|
||||
const add_files = async files => {
|
||||
let list_files = list.files;
|
||||
files.forEach(f => {
|
||||
list_files.push(f)
|
||||
})
|
||||
await update_list(list_files)
|
||||
dispatch("reload")
|
||||
|
||||
list.files = list_files // Update the view (and play animation)
|
||||
dispatch("update_list", list_files)
|
||||
}
|
||||
|
||||
const delete_file = async index => {
|
||||
let list_files = list.files
|
||||
list_files.splice(index, 1)
|
||||
await update_list(list_files)
|
||||
list.files = list_files
|
||||
|
||||
list.files = list_files // Update the view (and play animation)
|
||||
dispatch("update_list", list_files)
|
||||
}
|
||||
|
||||
const move_left = async index => {
|
||||
@@ -82,8 +36,9 @@ const move_left = async index => {
|
||||
}
|
||||
let f = list.files;
|
||||
[f[index], f[index-1]] = [f[index-1], f[index]];
|
||||
await update_list(f)
|
||||
list.files = f
|
||||
|
||||
list.files = f // Update the view (and play animation)
|
||||
dispatch("update_list", f)
|
||||
}
|
||||
const move_right = async index => {
|
||||
if (index >= list.files.length-1) {
|
||||
@@ -91,17 +46,33 @@ const move_right = async index => {
|
||||
}
|
||||
let f = list.files;
|
||||
[f[index], f[index+1]] = [f[index+1], f[index]];
|
||||
await update_list(f)
|
||||
list.files = f
|
||||
|
||||
list.files = f // Update the view (and play animation)
|
||||
dispatch("update_list", f)
|
||||
}
|
||||
|
||||
// Index of the file which is being hovered over. -1 is nothing and -2 is the
|
||||
// Add files button
|
||||
let hovering = -1
|
||||
let dragging = false
|
||||
const drag = (e, index) => {
|
||||
dragging = true
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
e.dataTransfer.setData('text/plain', index);
|
||||
}
|
||||
const drop = (e, index) => {
|
||||
hovering = -1
|
||||
dragging = false
|
||||
|
||||
if (e.dataTransfer.files.length !== 0) {
|
||||
// This is not a rearrangement, this is a file upload
|
||||
dispatch("upload_files", e.dataTransfer.files)
|
||||
return
|
||||
} else if (index === -2) {
|
||||
return
|
||||
}
|
||||
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
let start = parseInt(e.dataTransfer.getData("text/plain"));
|
||||
let list_files = list.files
|
||||
@@ -116,11 +87,32 @@ const drop = (e, index) => {
|
||||
return; // Nothing changed
|
||||
}
|
||||
|
||||
update_list(list_files)
|
||||
dispatch("update_list", list_files)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="gallery">
|
||||
{#if list.can_edit}
|
||||
<div class="add_button"
|
||||
on:drop|preventDefault={e => drop(e, -2)}
|
||||
on:dragover|preventDefault|stopPropagation
|
||||
on:dragenter={() => hovering = -2}
|
||||
on:dragend={() => {hovering = -1}}
|
||||
class:highlight={!dragging && hovering === -2}
|
||||
>
|
||||
<button on:click={e => dispatch("pick_files")} style="font-size: 1.5em; cursor: pointer;">
|
||||
<i class="icon">cloud_upload</i>
|
||||
<br/>
|
||||
Upload files
|
||||
</button>
|
||||
<button on:click={file_picker.open} style="font-size: 1.5em; cursor: pointer;">
|
||||
<i class="icon">add</i>
|
||||
<br/>
|
||||
Add files
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#each list.files as file, index (file)}
|
||||
<a
|
||||
href="#item={index}"
|
||||
@@ -129,10 +121,10 @@ const drop = (e, index) => {
|
||||
on:dragstart={e => drag(e, index)}
|
||||
on:drop|preventDefault={e => drop(e, index)}
|
||||
on:dragover|preventDefault|stopPropagation
|
||||
on:dragenter={() => {hovering = index}}
|
||||
on:dragend={() => {hovering = -1}}
|
||||
class:highlight={hovering === index}
|
||||
animate:flip={{duration: 500}}>
|
||||
on:dragenter={() => hovering = index}
|
||||
on:dragend={() => {hovering = -1; dragging = false}}
|
||||
class:highlight={dragging && hovering === index}
|
||||
animate:flip={{duration: 400}}>
|
||||
<div
|
||||
class="icon_container"
|
||||
class:editing={list.can_edit}
|
||||
@@ -159,14 +151,6 @@ const drop = (e, index) => {
|
||||
{file.name}
|
||||
</a>
|
||||
{/each}
|
||||
|
||||
{#if list.can_edit}
|
||||
<button class="file" on:click={file_picker.open} style="font-size: 1.5em; cursor: pointer;">
|
||||
<i class="icon">add</i>
|
||||
<br/>
|
||||
Add files
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<FilePicker
|
||||
@@ -242,4 +226,23 @@ const drop = (e, index) => {
|
||||
.button_row>.separator {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.add_button{
|
||||
width: 200px;
|
||||
max-width: 42%;
|
||||
height: 200px;
|
||||
margin: 8px;
|
||||
border-radius: 8px;
|
||||
background: var(--body_color);
|
||||
text-align: center;
|
||||
line-height: 1.2em;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
color: var(--body_text_color);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.add_button > * {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
</style>
|
||||
|
82
svelte/src/file_viewer/ListUpdater.svelte
Normal file
82
svelte/src/file_viewer/ListUpdater.svelte
Normal file
@@ -0,0 +1,82 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import UploadWidget from "../util/upload_widget/UploadWidget.svelte";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let list = {
|
||||
title: "",
|
||||
files: [],
|
||||
info_href: "",
|
||||
}
|
||||
|
||||
export const update = async new_files => {
|
||||
dispatch("loading", true)
|
||||
|
||||
// If the list is empty we simply delete it
|
||||
if (list.files.length === 0) {
|
||||
try {
|
||||
let resp = await fetch(list.info_href, {method: "DELETE"})
|
||||
if (resp.status >= 400) {
|
||||
throw (await resp.json()).message
|
||||
}
|
||||
window.close()
|
||||
} catch (err) {
|
||||
alert("Failed to delete album: "+err)
|
||||
} finally {
|
||||
dispatch("loading", false)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let listjson = {
|
||||
title: list.title,
|
||||
files: [],
|
||||
}
|
||||
new_files.forEach(f => {
|
||||
listjson.files.push({
|
||||
id: f.id,
|
||||
})
|
||||
})
|
||||
|
||||
try {
|
||||
const resp = await fetch(
|
||||
list.info_href,
|
||||
{ method: "PUT", body: JSON.stringify(listjson) },
|
||||
);
|
||||
if (resp.status >= 400) {
|
||||
throw (await resp.json()).message
|
||||
}
|
||||
} catch (err) {
|
||||
alert("Failed to update album: "+err)
|
||||
} finally {
|
||||
dispatch("loading", false)
|
||||
dispatch("reload")
|
||||
}
|
||||
}
|
||||
|
||||
let upload_widget
|
||||
export const pick_files = () => upload_widget.pick_files()
|
||||
export const upload_files = files => upload_widget.upload_files(files)
|
||||
|
||||
const uploads_finished = async (file_ids) => {
|
||||
let list_files = list.files;
|
||||
file_ids.forEach(id => {
|
||||
list_files.push({id: id})
|
||||
})
|
||||
|
||||
await update(list_files)
|
||||
}
|
||||
|
||||
const paste = (e) => {
|
||||
if (e.clipboardData.files.length !== 0) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
upload_widget.upload_files(e.clipboardData.files)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:paste={paste}/>
|
||||
|
||||
<UploadWidget bind:this={upload_widget} on:uploads_finished={e => uploads_finished(e.detail)}/>
|
Reference in New Issue
Block a user