Allow drag-and-drop uploading in album viewer

This commit is contained in:
2023-03-21 23:37:46 +01:00
parent b2101b3243
commit 236e282777
7 changed files with 195 additions and 96 deletions

View File

@@ -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>

View File

@@ -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>

View 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)}/>