File mode: support drag drop and fix cancellation of uploads

This commit is contained in:
N-Pex 2023-07-06 11:18:26 +02:00
parent fbacf68651
commit 2f29b72594
4 changed files with 125 additions and 67 deletions

View file

@ -16,7 +16,8 @@
v-on:close="showRecorder = false" v-on:file="onVoiceRecording" :sendTypingIndicators="useVoiceMode" />
<FileDropLayout class="file-drop-root" v-if="useFileModeNonAdmin" :room="room"
v-on:add-file="showAttachmentPicker()"
v-on:pick-file="showAttachmentPicker()"
v-on:add-file="addAttachment($event)"
v-on:remove-file="currentFileInputs.splice($event, 1)"
v-on:reset="resetAttachments"
:attachments="currentFileInputs"
@ -1146,6 +1147,10 @@ export default {
this.currentSendError = null;
},
addAttachment(file) {
this.handleFileReader(null, file);
},
resetAttachments() {
this.cancelSendAttachment();
},

View file

@ -6,8 +6,10 @@
<v-icon>$vuetify.icons.ic_lock</v-icon>
<div class="file-drop-title">{{ $t("file_mode.secure_file_send") }}</div>
</div>
<div class="background">
<v-btn @click="$emit('add-file')" class="large">{{ $t("file_mode.choose_files") }}</v-btn>
<div :class="{ 'background': true, 'drop-target': dropTarget }" @drop.prevent="filesDropped"
@dragover.prevent="dropTarget = true" @dragleave.prevent="dropTarget = false"
@dragenter.prevent="dropTarget = true">
<v-btn @click="$emit('pick-file')" class="large">{{ $t("file_mode.choose_files") }}</v-btn>
<div class="file-format-info">{{ $t("file_mode.any_file_format_accepted") }}</div>
</div>
</template>
@ -15,7 +17,9 @@
<!-- ATTACHMENT SELECTION MODE -->
<template v-if="attachments && attachments.length > 0 && status == mainStatuses.SELECTING">
<div class="attachment-wrapper" ref="attachmentWrapper">
<div class="file-drop-current-item">
<div :class="{ 'file-drop-current-item': true, 'drop-target': dropTarget }" @drop.prevent="filesDropped"
@dragover.prevent="dropTarget = true" @dragleave.prevent="dropTarget = false"
@dragenter.prevent="dropTarget = true">
<v-img v-if="currentItemHasImagePreview" :src="attachments[currentItemIndex].image" />
<div v-else class="filename">{{ attachments[currentItemIndex].name }}</div>
</div>
@ -28,7 +32,7 @@
</div>
</div>
<div class="file-drop-thumbnail noborder">
<div class="add clickable" @click.stop="$emit('add-file')">
<div class="add clickable" @click.stop="$emit('pick-file')">
+
</div>
</div>
@ -46,11 +50,12 @@
</template>
<!-- ATTACHMENT SENDING/SENT MODE -->
<template v-if="attachments && attachments.length > 0 && (status == mainStatuses.SENDING || status == mainStatuses.SENT)">
<template
v-if="attachments && attachments.length > 0 && (status == mainStatuses.SENDING || status == mainStatuses.SENT)">
<div class="attachment-wrapper">
<div class="file-drop-sent-stack" ref="stackContainer">
<div v-if="status == mainStatuses.SENDING && countSent == 0" class="no-items">
<div class="file-drop-stack-item" :style="stackItemTransform(null, -1)"></div>
<div class="file-drop-stack-item direct" :style="stackItemTransform(null, -1)"></div>
<div>{{ $t('file_mode.sending_progress') }}</div>
</div>
<div v-else v-for="(item, index) in sentItems" :key="item.id" class="file-drop-stack-item animated"
@ -130,6 +135,7 @@ export default {
FAILED: 3,
}),
sendInfo: [],
dropTarget: false,
};
},
mounted() {
@ -173,6 +179,14 @@ export default {
},
},
methods: {
filesDropped(e) {
this.dropTarget = false;
let droppedFiles = e.dataTransfer.files;
if (!droppedFiles) return;
([...droppedFiles]).forEach(f => {
this.$emit('add-file', f);
});
},
scrollToBottom() {
const el = this.$refs.attachmentWrapper;
if (el) {
@ -216,38 +230,56 @@ export default {
util.sendTextMessage(this.$matrix.matrixClient, this.room.roomId, text)
.then((eventId) => {
// Use the eventId as a thread root for all the media
const promises = this.sendInfo.map(item => util.sendImage(this.$matrix.matrixClient, this.room.roomId, item.attachment, ({ loaded, total }) => {
if (loaded == total) {
item.progress = 100;
} else if (total > 0) {
item.progress = 100 * loaded / total;
let promiseChain = Promise.resolve();
const getItemPromise = (index) => {
if (index < this.sendInfo.length) {
const item = this.sendInfo[index];
if (item.status !== this.statuses.INITIAL) {
return getItemPromise(++index);
}
const itemPromise = util.sendImage(this.$matrix.matrixClient, this.room.roomId, item.attachment, ({ loaded, total }) => {
if (loaded == total) {
item.progress = 100;
} else if (total > 0) {
item.progress = 100 * loaded / total;
}
}, eventId)
.then(() => {
// Look at last item rotation, flipping the sign on this, so looks more like a true stack
let signR = 1;
let signX = 1;
let signY = 1;
if (this.sentItems.length > 0) {
if (this.sentItems[0].randomRotation >= 0) {
signR = -1;
}
if (this.sentItems[0].randomTranslationX >= 0) {
signX = -1;
}
if (this.sentItems[0].randomTranslationY >= 0) {
signY = -1;
}
}
item.randomRotation = signR * (2 + Math.random() * 10);
item.randomTranslationX = signX * Math.random() * 20;
item.randomTranslationY = signY * Math.random() * 20;
item.status = this.statuses.SENT;
item.statusDate = Date.now;
}).catch(ignorederr => {
if (item.promise.aborted) {
item.status = this.statuses.CANCELED;
} else {
console.error("ERROR", ignorederr);
item.status = this.statuses.FAILED;
}
});
item.promise = itemPromise;
return itemPromise.then(() => getItemPromise(++index));
}
}, eventId).then(() => {
// Look at last item rotation, flipping the sign on this, so looks more like a true stack
let signR = 1;
let signX = 1;
let signY = 1;
if (this.sentItems.length > 0) {
if (this.sentItems[0].randomRotation >= 0) {
signR = -1;
}
if (this.sentItems[0].randomTranslationX >= 0) {
signX = -1;
}
if (this.sentItems[0].randomTranslationY >= 0) {
signY = -1;
}
}
item.randomRotation = signR * (2 + Math.random() * 10);
item.randomTranslationX = signX * Math.random() * 20;
item.randomTranslationY = signY * Math.random() * 20;
item.status = this.statuses.SENT;
item.statusDate = Date.now;
}).catch(ignorederr => {
console.error("ERROR", ignorederr);
item.status = this.statuses.FAILED;
}));
return Promise.allSettled(promises)
else return Promise.resolve();
};
return promiseChain.then(() => getItemPromise(0));
})
.then(() => {
this.status = this.mainStatuses.SENT;
@ -257,7 +289,9 @@ export default {
});
},
cancelSendingItem(item) {
// TODO
if (item.promise && item.status == this.statuses.INITIAL) {
item.promise.abort();
}
item.status = this.statuses.CANCELED;
},
checkDone() {