Work on attachments
This commit is contained in:
parent
ec79a33eab
commit
842c87dc96
28 changed files with 2714 additions and 798 deletions
|
|
@ -38,7 +38,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="file-drop-input-container">
|
||||
<v-textarea ref="input" full-width variant="solo" flat auto-grow v-model="messageInput" no-resize class="input-area-text"
|
||||
<v-textarea theme="dark" ref="input" full-width variant="solo" flat auto-grow v-model="messageInput" no-resize class="input-area-text"
|
||||
rows="1" :placeholder="$t('file_mode.add_a_message')" hide-details background-color="transparent"
|
||||
v-on:keydown.enter.prevent="() => {
|
||||
sendCurrentTextMessage();
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
<div class="file-drop-sending-item" v-for="(info, index) in attachmentsSending" :key="index">
|
||||
<v-img v-if="info.preview" :src="info.preview" />
|
||||
<div v-else class="filename">{{ info.attachment.name }}</div>
|
||||
<v-progress-linear :value="info.progress"></v-progress-linear>
|
||||
<v-progress-linear :model-value="info.progress"></v-progress-linear>
|
||||
<div class="file-drop-cancel clickable" @click.stop="cancelSendAttachmentItem(info)">
|
||||
<v-icon size="14" color="white">close</v-icon>
|
||||
</div>
|
||||
|
|
@ -105,7 +105,7 @@
|
|||
|
||||
<script>
|
||||
import messageMixin from "../messages/messageMixin";
|
||||
import sendAttachmentsMixin from "../sendAttachmentsMixin";
|
||||
import sendAttachmentsMixin from "../sendAttachmentsMixin.ts";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
|
||||
export default {
|
||||
|
|
@ -163,9 +163,7 @@ export default {
|
|||
this.dropTarget = false;
|
||||
let droppedFiles = e.dataTransfer.files;
|
||||
if (!droppedFiles) return;
|
||||
([...droppedFiles]).forEach(f => {
|
||||
this.$emit('add-file', f);
|
||||
});
|
||||
this.$emit('add-files', [...droppedFiles]);
|
||||
},
|
||||
scrollToBottom() {
|
||||
const el = this.$refs.attachmentWrapper;
|
||||
|
|
|
|||
322
src/components/file_mode/SendAttachmentsLayout.vue
Normal file
322
src/components/file_mode/SendAttachmentsLayout.vue
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
<template>
|
||||
<div v-bind="{ ...$attrs }" class="send-attachments">
|
||||
<div class="send-attachments__title">{{ $t("message.send_attachements_dialog_title") }}</div>
|
||||
|
||||
<!-- ATTACHMENT SELECTION MODE -->
|
||||
<template v-if="attachments && attachments.length > 0 && status == mainStatuses.SELECTING">
|
||||
<div class="attachment-wrapper" ref="attachmentWrapper" v-if="currentAttachment">
|
||||
<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="currentAttachment.src && currentAttachment.status === 'loaded'" :src="currentAttachment.src" />
|
||||
<div v-else class="filename">
|
||||
<div>{{ currentAttachment.file.name }}</div>
|
||||
<div v-if="currentAttachment.status === 'loading'" style="font-size: 0.7em; opacity: 0.7">
|
||||
{{ $t("message.preparing_to_upload") }}
|
||||
<v-progress-linear indeterminate class="mb-0"></v-progress-linear>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="send-attachments__current-item__info">
|
||||
<div class="send-attachments__current-item__info__size">
|
||||
<span
|
||||
v-if="currentAttachment.scaledFile && currentAttachment.useScaled && currentAttachment.scaledDimensions"
|
||||
>
|
||||
{{ currentAttachment.scaledDimensions.width }} x {{ currentAttachment.scaledDimensions.height }}</span
|
||||
>
|
||||
<span v-else-if="currentAttachment.dimensions">
|
||||
{{ currentAttachment.dimensions.width }} x {{ currentAttachment.dimensions.height }}
|
||||
</span>
|
||||
|
||||
<span v-if="currentAttachment.scaledFile && currentAttachment.useScaled">
|
||||
({{ formatBytes(currentAttachment.scaledFile.size) }})
|
||||
</span>
|
||||
<span v-else> ({{ formatBytes(currentAttachment.file.size) }}) </span>
|
||||
|
||||
<span class="send-attachments__current-item__info__size__filename" v-if="currentAttachment.src && currentAttachment.file.name">
|
||||
- {{ currentAttachment.file.name }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<v-switch
|
||||
v-if="currentAttachment.scaledFile"
|
||||
:label="$t('message.scale_image')"
|
||||
v-model="currentAttachment.useScaled"
|
||||
:disabled="currentAttachment.sendInfo !== undefined"
|
||||
/>
|
||||
|
||||
<C2PABadge :proof="currentAttachment.proof" />
|
||||
</div>
|
||||
|
||||
<div class="file-drop-thumbnail-container">
|
||||
<div
|
||||
:class="{ 'file-drop-thumbnail': true, clickable: true, current: id == currentItemIndex }"
|
||||
@click="currentItemIndex = id"
|
||||
v-for="(currentImageInput, id) in attachments"
|
||||
:key="id"
|
||||
>
|
||||
<v-img v-if="currentImageInput && currentImageInput.src" :src="currentImageInput.src" />
|
||||
<div v-if="currentItemIndex == id" class="remove clickable" @click.stop="$emit('remove-file', id)">
|
||||
<v-icon>$vuetify.icons.ic_trash</v-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-drop-thumbnail noborder">
|
||||
<div class="add clickable" @click.stop="$emit('pick-file')">+</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-drop-input-container">
|
||||
<v-textarea
|
||||
ref="input"
|
||||
full-width
|
||||
variant="solo"
|
||||
flat
|
||||
auto-grow
|
||||
v-model="messageInput"
|
||||
no-resize
|
||||
class="input-area-text"
|
||||
rows="1"
|
||||
:placeholder="$t('file_mode.add_a_message')"
|
||||
hide-details
|
||||
background-color="transparent"
|
||||
v-on:keydown.enter.prevent="
|
||||
() => {
|
||||
sendAll();
|
||||
}
|
||||
"
|
||||
/>
|
||||
<div class="input-container__buttons">
|
||||
<v-btn @click="close">{{ $t("menu.cancel") }}</v-btn>
|
||||
<v-btn @click="sendAll" :disabled="!attachments || attachments.length == 0">{{ $t("menu.send") }}</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- ATTACHMENT SENDING/SENT MODE -->
|
||||
<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 && attachmentsSentCount == 0" class="no-items">
|
||||
<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="(info, index) in attachmentsSent"
|
||||
:key="info.file.name"
|
||||
class="file-drop-stack-item animated"
|
||||
:style="stackItemTransform(info, index)"
|
||||
>
|
||||
<v-img v-if="info.src" :src="info.src" />
|
||||
</div>
|
||||
<div v-if="status == mainStatuses.SENT" class="items-sent" :style="stackItemTransform(null, -1)">
|
||||
<v-icon>$vuetify.icons.ic_check_circle</v-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Middle section -->
|
||||
<div v-if="status == mainStatuses.SENDING" class="file-drop-sending-container">
|
||||
<div class="file-drop-sending-item" v-for="(attachment) in attachmentsSending" :key="attachment.file.name">
|
||||
<v-img v-if="attachment.src" :src="attachment.src" />
|
||||
<div v-else class="filename">{{ attachment.file.name }}</div>
|
||||
<v-progress-linear :model-value="attachment.sendInfo?.progress ?? 0"></v-progress-linear>
|
||||
<div class="file-drop-cancel clickable" @click.stop="cancelSendAttachmentItem(attachment)">
|
||||
<v-icon size="14" color="white">close</v-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="status == mainStatuses.SENT" class="file-drop-sending-container">
|
||||
<div class="file-drop-files-sent">
|
||||
{{
|
||||
$t(
|
||||
messageInput && messageInput.length > 0
|
||||
? "file_mode.files_sent_with_note"
|
||||
: "file_mode.files_sent",
|
||||
attachmentsSent.length
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
<div class="file-drop-section">
|
||||
<v-textarea
|
||||
disabled
|
||||
full-width
|
||||
variant="solo"
|
||||
flat
|
||||
auto-grow
|
||||
v-model="messageInput"
|
||||
no-resize
|
||||
class="input-area-text"
|
||||
rows="1"
|
||||
hide-details
|
||||
background-color="transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom section -->
|
||||
<div v-if="status == mainStatuses.SENDING" class="file-drop-sending-input-container">
|
||||
<v-textarea
|
||||
disabled
|
||||
full-width
|
||||
variant="solo"
|
||||
flat
|
||||
auto-grow
|
||||
v-model="messageInput"
|
||||
no-resize
|
||||
class="input-area-text"
|
||||
rows="1"
|
||||
:placeholder="$t('file_mode.add_a_message')"
|
||||
hide-details
|
||||
background-color="transparent"
|
||||
/>
|
||||
<v-btn
|
||||
>{{ $t("file_mode.sending")
|
||||
}}<v-progress-circular indeterminate size="18" width="2" color="#4642F1"></v-progress-circular
|
||||
></v-btn>
|
||||
</div>
|
||||
<div v-else-if="status == mainStatuses.SENT" class="file-drop-sent-input-container">
|
||||
<v-btn class="close" @click.stop="close">{{ $t("file_mode.close") }}</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import messageMixin from "../messages/messageMixin";
|
||||
import sendAttachmentsMixin from "../sendAttachmentsMixin";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import type { PropType } from "vue";
|
||||
import { Attachment } from "../../models/attachment";
|
||||
import C2PABadge from "../c2pa/C2PABadge.vue";
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [messageMixin, sendAttachmentsMixin],
|
||||
components: { C2PABadge },
|
||||
emits: ["add-files", "remove-file", "pick-file", "close"],
|
||||
props: {
|
||||
attachments: {
|
||||
type: Array as PropType<Attachment[]>,
|
||||
default: function () {
|
||||
return [] as Attachment[];
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentItemIndex: 0,
|
||||
messageInput: "",
|
||||
mainStatuses: Object.freeze({
|
||||
SELECTING: 0,
|
||||
SENDING: 1,
|
||||
SENT: 2,
|
||||
}),
|
||||
status: 0,
|
||||
dropTarget: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$audioPlayer.setAutoplay(false);
|
||||
},
|
||||
computed: {
|
||||
currentAttachment(): Attachment {
|
||||
return this.attachments[this.currentItemIndex];
|
||||
},
|
||||
currentItemHasImagePreview() {
|
||||
return (
|
||||
this.currentItemIndex >= 0 &&
|
||||
this.currentItemIndex < this.attachments.length &&
|
||||
this.attachments[this.currentItemIndex].src
|
||||
);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
attachments(newValue, oldValue) {
|
||||
// Added or removed?
|
||||
if (newValue && oldValue && newValue.length > oldValue.length) {
|
||||
this.currentItemIndex = oldValue.length;
|
||||
} else if (newValue) {
|
||||
this.currentItemIndex = newValue.length - 1;
|
||||
}
|
||||
},
|
||||
messageInput() {
|
||||
this.scrollToBottom();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
filesDropped(e: DragEvent) {
|
||||
this.dropTarget = false;
|
||||
let droppedFiles: FileList | undefined = e.dataTransfer?.files;
|
||||
if (!droppedFiles) return;
|
||||
this.$emit("add-files", [... droppedFiles]);
|
||||
},
|
||||
scrollToBottom() {
|
||||
const el = this.$refs.attachmentWrapper;
|
||||
if (el) {
|
||||
// Ugly - need to wait until input is auto-sized, THEN scroll to bottom.
|
||||
//
|
||||
this.$nextTick(() => {
|
||||
this.$nextTick(() => {
|
||||
this.$nextTick(() => {
|
||||
el.scrollTop = el.scrollHeight;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
formatBytes(bytes: number) {
|
||||
return prettyBytes(bytes);
|
||||
},
|
||||
close() {
|
||||
this.sendingAttachments = [];
|
||||
this.status = this.mainStatuses.SELECTING;
|
||||
this.messageInput = "";
|
||||
this.currentItemIndex = 0;
|
||||
this.$emit("close");
|
||||
},
|
||||
sendAll() {
|
||||
this.status = this.mainStatuses.SENDING;
|
||||
this.sendAttachments(
|
||||
this.messageInput && this.messageInput.length > 0 ? this.messageInput : this.$t("file_mode.files"),
|
||||
this.attachments
|
||||
).then(() => {
|
||||
this.status = this.mainStatuses.SENT;
|
||||
});
|
||||
},
|
||||
stackItemTransform(item, index) {
|
||||
const size =
|
||||
0.6 *
|
||||
(this.$refs.stackContainer
|
||||
? Math.min(this.$refs.stackContainer.clientWidth, this.$refs.stackContainer.clientHeight)
|
||||
: 176);
|
||||
let transform = "";
|
||||
if (item != null && index != -1) {
|
||||
transform =
|
||||
"transform: rotate(" +
|
||||
item.randomRotation +
|
||||
"deg) translate(" +
|
||||
item.randomTranslationX +
|
||||
"px," +
|
||||
item.randomTranslationY +
|
||||
"px); z-index:" +
|
||||
(index + 2) +
|
||||
";";
|
||||
}
|
||||
return transform + "width:" + size + "px;height:" + size + "px;border-radius:" + size / 8 + "px";
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use "@/assets/css/chat.scss" as *;
|
||||
@use "@/assets/css/sendattachments.scss" as *;
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue