mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 03:22:19 +03:00
UBER-924: Fix file upload progress (#3757)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
3e8cbb18af
commit
e1cdd7636a
@ -14,10 +14,22 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import Spinner from './Spinner.svelte'
|
||||
|
||||
export let shrink: boolean = false
|
||||
export let label: string = ''
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let timer: any
|
||||
onMount(() => {
|
||||
timer = setTimeout(() => {
|
||||
dispatch('progress')
|
||||
}, 50)
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="spinner-container" class:fullSize={!shrink}>
|
||||
|
@ -17,7 +17,7 @@
|
||||
import type { Doc } from '@hcengineering/core'
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { ActionIcon, IconAdd, Label } from '@hcengineering/ui'
|
||||
import { ActionIcon, IconAdd, Label, Loading } from '@hcengineering/ui'
|
||||
import { AttachmentPresenter } from '..'
|
||||
import attachment from '../plugin'
|
||||
import { uploadFile } from '../utils'
|
||||
@ -31,6 +31,8 @@
|
||||
|
||||
let docs: Attachment[] = []
|
||||
|
||||
let progress = false
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(
|
||||
attachment.class.Attachment,
|
||||
@ -59,14 +61,19 @@
|
||||
})
|
||||
}
|
||||
|
||||
function fileSelected () {
|
||||
async function fileSelected (): Promise<void> {
|
||||
progress = true
|
||||
|
||||
const list = inputFile.files
|
||||
if (list === null || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) createAttachment(file)
|
||||
if (file !== null) {
|
||||
await createAttachment(file)
|
||||
}
|
||||
}
|
||||
inputFile.value = ''
|
||||
progress = false
|
||||
}
|
||||
|
||||
let inputFile: HTMLInputElement
|
||||
@ -94,7 +101,11 @@
|
||||
</div>
|
||||
{#if canAdd}
|
||||
<div>
|
||||
<ActionIcon size={'medium'} icon={IconAdd} action={add} />
|
||||
{#if progress}
|
||||
<Loading />
|
||||
{:else}
|
||||
<ActionIcon size={'medium'} icon={IconAdd} action={add} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { Attachment } from '@hcengineering/attachment'
|
||||
import { showPopup, closeTooltip, Label, getIconSize2x } from '@hcengineering/ui'
|
||||
import { showPopup, closeTooltip, Label, getIconSize2x, Loading } from '@hcengineering/ui'
|
||||
import presentation, { PDFViewer, getFileUrl } from '@hcengineering/presentation'
|
||||
import filesize from 'filesize'
|
||||
|
||||
@ -24,6 +24,8 @@
|
||||
export let removable: boolean = false
|
||||
export let showPreview = false
|
||||
|
||||
export let progress: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const maxLenght: number = 30
|
||||
@ -100,7 +102,11 @@
|
||||
class:image={isImage(value.type)}
|
||||
style={imgStyle}
|
||||
>
|
||||
{#if !isImage(value.type)}{iconLabel(value.name)}{/if}
|
||||
{#if progress}
|
||||
<div class="flex p-3">
|
||||
<Loading />
|
||||
</div>
|
||||
{:else if !isImage(value.type)}{iconLabel(value.name)}{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex-center icon">
|
||||
|
@ -19,9 +19,9 @@
|
||||
import { deleteFile, uploadFile } from '../utils'
|
||||
import attachment from '../plugin'
|
||||
import { IntlString, setPlatformStatus, unknownError, Asset } from '@hcengineering/platform'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import { createEventDispatcher, onDestroy, tick } from 'svelte'
|
||||
import { Account, Class, Doc, generateId, IdMap, Ref, Space, toIdMap } from '@hcengineering/core'
|
||||
import type { AnySvelteComponent } from '@hcengineering/ui'
|
||||
import { Loading, type AnySvelteComponent } from '@hcengineering/ui'
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import AttachmentPresenter from './AttachmentPresenter.svelte'
|
||||
|
||||
@ -59,6 +59,8 @@
|
||||
const newAttachments: Set<Ref<Attachment>> = new Set<Ref<Attachment>>()
|
||||
const removedAttachments: Set<Attachment> = new Set<Attachment>()
|
||||
|
||||
let progress = false
|
||||
|
||||
let refContainer: HTMLElement
|
||||
|
||||
$: objectId && updateAttachments(objectId)
|
||||
@ -128,23 +130,32 @@
|
||||
await client.addCollection(attachment.class.Attachment, space, objectId, _class, 'attachments', doc, doc._id)
|
||||
}
|
||||
|
||||
function fileSelected () {
|
||||
async function fileSelected (): Promise<void> {
|
||||
progress = true
|
||||
await tick()
|
||||
const list = inputFile.files
|
||||
if (list === null || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) createAttachment(file)
|
||||
if (file !== null) {
|
||||
await createAttachment(file)
|
||||
}
|
||||
}
|
||||
inputFile.value = ''
|
||||
progress = false
|
||||
}
|
||||
|
||||
function fileDrop (e: DragEvent) {
|
||||
async function fileDrop (e: DragEvent): Promise<void> {
|
||||
progress = true
|
||||
const list = e.dataTransfer?.files
|
||||
if (list === undefined || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) createAttachment(file)
|
||||
if (file !== null) {
|
||||
await createAttachment(file)
|
||||
}
|
||||
}
|
||||
progress = false
|
||||
}
|
||||
|
||||
async function removeAttachment (attachment: Attachment): Promise<void> {
|
||||
@ -221,7 +232,7 @@
|
||||
dispatch('update', { message: event.detail, attachments: attachments.size })
|
||||
}
|
||||
|
||||
function pasteAction (evt: ClipboardEvent): void {
|
||||
async function pasteAction (evt: ClipboardEvent): Promise<void> {
|
||||
let t: HTMLElement | null = evt.target as HTMLElement
|
||||
let allowed = false
|
||||
while (t != null) {
|
||||
@ -235,15 +246,17 @@
|
||||
}
|
||||
|
||||
const items = evt.clipboardData?.items ?? []
|
||||
progress = true
|
||||
for (const index in items) {
|
||||
const item = items[index]
|
||||
if (item.kind === 'file') {
|
||||
const blob = item.getAsFile()
|
||||
if (blob !== null) {
|
||||
createAttachment(blob)
|
||||
await createAttachment(blob)
|
||||
}
|
||||
}
|
||||
}
|
||||
progress = false
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -263,8 +276,13 @@
|
||||
on:dragleave={() => {}}
|
||||
on:drop|preventDefault|stopPropagation={fileDrop}
|
||||
>
|
||||
{#if attachments.size}
|
||||
{#if attachments.size || progress}
|
||||
<div class="flex-row-center list scroll-divider-color">
|
||||
{#if progress}
|
||||
<div class="flex p-3">
|
||||
<Loading />
|
||||
</div>
|
||||
{/if}
|
||||
{#each Array.from(attachments.values()) as attachment}
|
||||
<div class="item flex">
|
||||
<AttachmentPresenter
|
||||
|
@ -117,8 +117,7 @@
|
||||
_class={object._class}
|
||||
space={object.space}
|
||||
alwaysEdit
|
||||
on:attached={(e) => descriptionBox.saveNewAttachment(e.detail)}
|
||||
on:detached={(e) => descriptionBox.removeAttachmentById(e.detail)}
|
||||
useDirectAttachDelete
|
||||
showButtons
|
||||
on:blur={() => save(object, description)}
|
||||
on:changeContent={triggerSave}
|
||||
|
@ -25,6 +25,7 @@
|
||||
import AttachmentPresenter from './AttachmentPresenter.svelte'
|
||||
import AttachmentPreview from './AttachmentPreview.svelte'
|
||||
import { ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
|
||||
import Loading from '@hcengineering/ui/src/components/Loading.svelte'
|
||||
|
||||
export let objectId: Ref<Doc> | undefined = undefined
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
@ -46,6 +47,10 @@
|
||||
export let enableBackReferences: boolean = false
|
||||
export let isScrollable = true
|
||||
|
||||
export let useDirectAttachDelete = false
|
||||
|
||||
let progress = false
|
||||
|
||||
let draftKey = objectId ? `${objectId}_attachments` : undefined
|
||||
$: draftKey = objectId ? `${objectId}_attachments` : undefined
|
||||
|
||||
@ -164,6 +169,10 @@
|
||||
saveDraft()
|
||||
dispatch('attach', { action: 'saved', value: attachments.size })
|
||||
dispatch('attached', _id)
|
||||
|
||||
if (useDirectAttachDelete) {
|
||||
saveNewAttachment(_id)
|
||||
}
|
||||
return { file: uuid, type: file.type }
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
@ -176,33 +185,48 @@
|
||||
await client.addCollection(attachment.class.Attachment, space, objectId, _class, 'attachments', doc, doc._id)
|
||||
}
|
||||
|
||||
function fileSelected () {
|
||||
async function fileSelected (): Promise<void> {
|
||||
progress = true
|
||||
const list = inputFile.files
|
||||
if (list === null || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) createAttachment(file)
|
||||
if (file !== null) {
|
||||
await createAttachment(file)
|
||||
}
|
||||
}
|
||||
inputFile.value = ''
|
||||
progress = false
|
||||
}
|
||||
|
||||
export function fileDrop (e: DragEvent) {
|
||||
export async function fileDrop (e: DragEvent): Promise<void> {
|
||||
progress = true
|
||||
const list = e.dataTransfer?.files
|
||||
if (list !== undefined && list.length !== 0) {
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) createAttachment(file)
|
||||
if (file !== null) {
|
||||
await createAttachment(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
progress = false
|
||||
}
|
||||
|
||||
async function removeAttachment (attachment: Attachment): Promise<void> {
|
||||
if (useDirectAttachDelete) {
|
||||
progressItems.push(attachment._id)
|
||||
progressItems = progressItems
|
||||
await deleteAttachment(attachment)
|
||||
}
|
||||
removedAttachments.add(attachment)
|
||||
attachments.delete(attachment._id)
|
||||
attachments = attachments
|
||||
refInput.removeAttachment(attachment.file)
|
||||
saveDraft()
|
||||
dispatch('detached', attachment._id)
|
||||
|
||||
progressItems = progressItems.filter((it) => it !== attachment._id)
|
||||
}
|
||||
|
||||
async function deleteAttachment (attachment: Attachment): Promise<void> {
|
||||
@ -299,7 +323,7 @@
|
||||
return false
|
||||
}
|
||||
|
||||
export function pasteAction (evt: ClipboardEvent): void {
|
||||
export async function pasteAction (evt: ClipboardEvent): Promise<void> {
|
||||
if (!isAllowedPaste(evt)) {
|
||||
return
|
||||
}
|
||||
@ -310,7 +334,7 @@
|
||||
if (item.kind === 'file') {
|
||||
const blob = item.getAsFile()
|
||||
if (blob !== null) {
|
||||
createAttachment(blob)
|
||||
await createAttachment(blob)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -323,6 +347,9 @@
|
||||
size: attachments.size,
|
||||
values: attachments.size === 0 ? true : attachments
|
||||
})
|
||||
|
||||
let element: HTMLElement
|
||||
let progressItems: Ref<Doc>[] = []
|
||||
</script>
|
||||
|
||||
<input
|
||||
@ -373,7 +400,7 @@
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if attachments.size && enableAttachments}
|
||||
{#if (attachments.size && enableAttachments) || progress}
|
||||
<div class="flex-row-center list scroll-divider-color">
|
||||
{#each Array.from(attachments.values()) as attachment, index}
|
||||
<div class="item flex-center flex-no-shrink clear-mins">
|
||||
@ -384,13 +411,25 @@
|
||||
value={attachment}
|
||||
removable
|
||||
showPreview
|
||||
progress={progressItems.includes(attachment._id)}
|
||||
on:remove={(result) => {
|
||||
if (result !== undefined) removeAttachment(attachment)
|
||||
if (result !== undefined) {
|
||||
removeAttachment(attachment)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{#if progress}
|
||||
<div class="flex p-3" bind:this={element}>
|
||||
<Loading
|
||||
on:progress={() => {
|
||||
element.scrollIntoView({ behavior: 'auto', block: 'center', inline: 'center' })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -36,6 +36,8 @@
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
let objectId = generateId()
|
||||
|
||||
let progress = false
|
||||
|
||||
let copy: string = ''
|
||||
|
||||
const obj: Data<NewMessage> = {
|
||||
@ -82,6 +84,7 @@
|
||||
let inputFile: HTMLInputElement
|
||||
|
||||
function fileSelected () {
|
||||
progress = true
|
||||
const list = inputFile.files
|
||||
if (list === null || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
@ -89,17 +92,20 @@
|
||||
if (file !== null) createAttachment(file)
|
||||
}
|
||||
inputFile.value = ''
|
||||
progress = false
|
||||
}
|
||||
|
||||
function fileDrop (e: DragEvent) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
progress = true
|
||||
const list = e.dataTransfer?.files
|
||||
if (list === undefined || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) createAttachment(file)
|
||||
}
|
||||
progress = false
|
||||
}
|
||||
|
||||
async function createAttachment (file: File) {
|
||||
@ -187,11 +193,12 @@
|
||||
<Button
|
||||
icon={IconAttachment}
|
||||
kind={'ghost'}
|
||||
loading={progress}
|
||||
on:click={() => {
|
||||
inputFile.click()
|
||||
}}
|
||||
/>
|
||||
<Button label={plugin.string.Send} kind={'accented'} on:click={sendMsg} />
|
||||
<Button label={plugin.string.Send} kind={'accented'} disabled={progress} on:click={sendMsg} />
|
||||
</div>
|
||||
</div>
|
||||
{#if attachments.length}
|
||||
|
Loading…
Reference in New Issue
Block a user