Improved file uploader; uploads from the library can be selected

This commit is contained in:
Mawoka
2023-06-11 20:10:53 +02:00
parent 7fb71edc61
commit 9c0058f1c3
6 changed files with 183 additions and 45 deletions
+17
View File
@@ -222,6 +222,23 @@ async def list_images(
return return_items
@router.get("/list/last")
async def get_latest_images(count: int = 50, user: User = Depends(get_current_user)) -> list[PrivateStorageItem]:
if count > 50:
count = 50
items = (
await StorageItem.objects.filter(user=user)
.limit(count)
.select_related([StorageItem.quizzes, StorageItem.quiztivities])
.order_by(StorageItem.uploaded_at.desc())
.all()
)
return_items: list[PrivateStorageItem] = []
for item in items:
return_items.append(PrivateStorageItem.from_db_model(item))
return return_items
class ReturnGetStorageLimit(BaseModel):
limit: int
limit_reached: bool
+1 -1
View File
@@ -70,7 +70,7 @@
</script>
<div class="w-full max-h-full pb-20 px-20 h-full">
<div class="rounded-lg bg-white w-full h-full border-gray-500 drop-shadow-2xl dark:bg-gray-700">
<div class="rounded-lg bg-white w-full h-full border-gray-500 dark:bg-gray-700 shadow-2xl">
<div class="h-12 bg-gray-300 rounded-t-lg dark:bg-gray-500">
<div class="flex align-middle p-4 gap-3">
<span
+85 -27
View File
@@ -12,6 +12,7 @@
import Dashboard from '@uppy/dashboard';
import Compressor from '@uppy/compressor';
import { fade } from 'svelte/transition';
import BrownButton from '$lib/components/buttons/brown.svelte';
// CSS imports
import '@uppy/core/dist/style.css';
@@ -22,6 +23,7 @@
import type { EditorData } from '../quiz_types';
import { getLocalization } from '$lib/i18n';
import { onMount } from 'svelte';
import Library from '$lib/editor/uploader/Library.svelte';
const { t } = getLocalization();
@@ -34,6 +36,18 @@
// eslint-disable-next-line no-undef
let video_popup: undefined | WindowProxy = undefined;
let selected_type: AvailableUploadTypes | null = null;
// eslint-disable-next-line no-unused-vars
enum AvailableUploadTypes {
// eslint-disable-next-line no-unused-vars
Image,
// eslint-disable-next-line no-unused-vars
Video,
// eslint-disable-next-line no-unused-vars
Library
}
const uppy = new Uppy()
.use(DropTarget, {
target: document.body
@@ -73,6 +87,7 @@
console.log(selected_question, data);
modalOpen = false;
selected_type = null;
});
console.log(edit_id);
@@ -83,6 +98,7 @@
}
localStorage.removeItem('video_upload_id');
data.questions[selected_question].image = e.newValue;
selected_type = null;
});
});
@@ -97,42 +113,84 @@
video_popup = undefined;
});
};
const handle_on_click = (e: Event) => {
if (e.target === e.currentTarget) {
modalOpen = false;
selected_type = null;
}
};
onMount(() => {
window.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'Escape') {
modalOpen = false;
selected_type = null;
}
});
});
</script>
{#if modalOpen}
<div
class="w-full h-full absolute top-0 left-0 bg-opacity-60 z-20 flex justify-center"
transition:fade|local
class="w-screen h-screen fixed top-0 left-0 bg-opacity-50 bg-black z-20 flex justify-center"
on:click={handle_on_click}
transition:fade|local={{ duration: 100 }}
>
<div>
<div>
<button
type="button"
class="rounded-t-lg bg-black text-white px-1"
on:click={() => {
modalOpen = false;
}}
>Close
</button>
{#if video_upload}
<button
class="rounded-t-lg bg-black text-white px-1"
on:click={upload_video}
type="button"
>Upload video
</button>
{/if}
</div>
{#if video_popup}
<div class="flex w-full h-1/3 bg-white dark:bg-gray-700 p-4">
<h2 class="text-4xl m-auto">Popup open</h2>
{#if selected_type === null}
<div class="m-auto w-1/3 h-auto bg-white dark:bg-gray-700 p-4 rounded">
<h1 class="text-3xl text-center mb-4">Select the Upload Type</h1>
<div class="flex flex-row gap-4">
<div class="w-full">
<BrownButton
on:click={() => {
selected_type = AvailableUploadTypes.Image;
}}>Image</BrownButton
>
</div>
<div class="w-full">
<BrownButton
disabled={!video_upload}
on:click={() => {
selected_type = AvailableUploadTypes.Video;
}}
>Video
</BrownButton>
</div>
<div class="w-full">
<BrownButton
on:click={() => {
selected_type = AvailableUploadTypes.Library;
}}
>Library
</BrownButton>
</div>
</div>
{:else}
</div>
{:else if selected_type === AvailableUploadTypes.Image}
<div class="m-auto w-1/3 h-5/6" transition:fade|local={{ duration: 100 }}>
<div>
<SvelteDashboard {uppy} width="100%" {props} />
</div>
{/if}
</div>
</div>
{:else if selected_type === AvailableUploadTypes.Video}
<div
class="m-auto w-1/3 h-auto bg-white dark:bg-gray-700 p-4 rounded"
transition:fade|local={{ duration: 100 }}
>
<h1 class="text-3xl text-center mb-4">Upload a Video</h1>
{#if video_popup}
<p class="text-center">
The popup is open; have a look at it for further information
</p>
{:else}
<BrownButton on:click={upload_video} type="button">Upload video</BrownButton>
{/if}
</div>
{:else if selected_type === AvailableUploadTypes.Library}
<div>
<Library bind:data {selected_question} bind:modalOpen />
</div>
{/if}
</div>
{/if}
<div class="flex justify-center w-full pt-10" transition:fade|local>
@@ -0,0 +1,62 @@
<!--
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<script lang="ts">
import type { PrivateImageData } from '$lib/quiz_types';
import Spinner from '$lib/Spinner.svelte';
import type { EditorData } from '$lib/quiz_types';
import BrownButton from '$lib/components/buttons/brown.svelte';
export let data: EditorData;
export let selected_question: number;
export let modalOpen: boolean;
const fetch_images = async (): Promise<PrivateImageData> => {
const response = await fetch('/api/v1/storage/list/last?count=50');
return await response.json();
};
let image_fetch = fetch_images();
const set_image = (id: string) => {
if (selected_question === undefined) {
data.cover_image = id;
} else if (selected_question === -1) {
data.background_image = id;
} else {
data.questions[selected_question].image = id;
}
modalOpen = false;
};
</script>
{#await image_fetch}
<Spinner />
{:then images}
<div class="flex w-screen p-8 h-screen">
<div
class="flex flex-col w-1/3 m-auto overflow-scroll h-full rounded p-4 gap-4 bg-white dark:bg-gray-700"
>
{#each images as image}
<div class="rounded border-2 border-[#B07156] p-2 flex-col flex gap-2">
<div>
<img
src="/api/v1/storage/download/{image.id}"
loading="lazy"
alt={image.alt_text}
class="object-contain w-full h-full max-h-full rounded"
/>
</div>
<p class="text-center">{image.filename ?? 'No name available'}</p>
<BrownButton
on:click={() => {
set_image(image.id);
}}>Select</BrownButton
>
</div>
{/each}
</div>
</div>
{/await}
+16
View File
@@ -88,3 +88,19 @@ export interface EditorData {
background_color?: string;
background_image?: string;
}
export interface PrivateImageData {
id: string;
uploaded_at: string;
mime_type: string;
hash?: string;
size?: number;
deleted_at?: string;
alt_text?: string;
filename?: string;
thumbhash?: string;
server?: string;
imported: boolean;
quizzes: { id: string }[];
quiztivities: { id: string }[];
}
+2 -17
View File
@@ -4,26 +4,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import type { PageLoad } from './$types';
export interface ImageData {
id: string;
uploaded_at: string;
mime_type: string;
hash?: string;
size?: number;
deleted_at?: string;
alt_text?: string;
filename?: string;
thumbhash?: string;
server?: string;
imported: boolean;
quizzes: { id: string }[];
quiztivities: { id: string }[];
}
import type { PrivateImageData } from '$lib/quiz_types';
export const load = (async ({ fetch }) => {
const res = await fetch('/api/v1/storage/list');
const json: ImageData[] = await res.json();
const json: PrivateImageData[] = await res.json();
return {
images: json
};