338 lines
13 KiB
Vue
338 lines
13 KiB
Vue
<template>
|
|
<div v-bind="{ ...$attrs }" class="send-attachments">
|
|
<v-btn v-if="!fileModeMode" class="back-button clickable" icon="arrow_back" size="default" elevation="0"
|
|
@click.stop="close" :disabled="backButtonDisabled" variant="flat"></v-btn>
|
|
|
|
<div v-if="fileModeMode" class="title file-drop">
|
|
<v-icon>$vuetify.icons.ic_lock</v-icon>
|
|
<div class="file-drop-title">{{ $t("file_mode.secure_file_send") }}</div>
|
|
</div>
|
|
<div v-else class="title">{{ title }}</div>
|
|
|
|
<!-- No attachments view -->
|
|
<template v-if="fileModeMode && status == mainStatuses.SELECTING && batch.attachments.length == 0">
|
|
<div :class="{ 'file-drop-choose-files': 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')" size="large">{{ $t("file_mode.choose_files") }}</v-btn>
|
|
<div class="file-format-info">{{ $t("file_mode.any_file_format_accepted") }}</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- ATTACHMENT SELECTION MODE -->
|
|
<template v-else-if="status == mainStatuses.SELECTING">
|
|
<div :class="{ 'send-attachments__selecting__current-item': true, 'drop-target': dropTarget }"
|
|
@drop.prevent="filesDropped" @dragover.prevent="dropTarget = true" @dragleave.prevent="dropTarget = false"
|
|
@dragenter.prevent="dropTarget = true">
|
|
<ThumbnailView :item="currentAttachment">
|
|
<template v-slot:decorator v-if="currentAttachment && currentAttachment.status === 'loading'">
|
|
<div style="font-size: 0.7em; opacity: 0.7">
|
|
<v-progress-circular indeterminate class="mb-0"></v-progress-circular>
|
|
</div>
|
|
</template>
|
|
<template v-slot:decorator v-else-if="showCCIcon">
|
|
<v-icon style="width:24px;height:24px">$vuetify.icons.ic_cr</v-icon>
|
|
</template>
|
|
</ThumbnailView>
|
|
|
|
</div>
|
|
<div class="file-drop-thumbnail-container">
|
|
<v-tooltip location="top" v-for="(attachment, index) in batch.attachments" :key="index">
|
|
<template v-slot:activator="{ props }">
|
|
<v-badge :model-value="batch.isTooLarge(attachment)" color="error">
|
|
<template v-slot:badge><span v-bind="props"> </span></template>
|
|
<div :class="{ 'file-drop-thumbnail': true, clickable: true, current: index == currentItemIndex }" @click="
|
|
() => {
|
|
currentItemIndex = index;
|
|
}
|
|
">
|
|
<v-img v-if="attachment && attachment.src" :src="attachment.src" />
|
|
<div v-if="currentItemIndex == index" class="remove clickable"
|
|
@click.stop="batch.removeAttachment(attachment)">
|
|
<v-icon>$vuetify.icons.ic_trash</v-icon>
|
|
</div>
|
|
</div>
|
|
</v-badge>
|
|
</template>
|
|
<span>{{ $t("message.upload_file_too_large") }}</span>
|
|
</v-tooltip>
|
|
<div class="file-drop-thumbnail noborder">
|
|
<div class="add clickable" @click.stop="$emit('pick-file')">+</div>
|
|
</div>
|
|
</div>
|
|
<div class="file-drop-input-container">
|
|
<div class="file-drop-input-container__input">
|
|
<v-text-field ref="input" full-width variant="solo" flat v-model="messageInput" no-resize
|
|
class="input-area-text" rows="1" :placeholder="$t('file_mode.add_a_message')" hide-details color="white"
|
|
background-color="transparent" />
|
|
<v-btn class="send-button clickable" icon="arrow_upward" size="default" elevation="0" color="black"
|
|
@click.stop="sendAll" :disabled="sendButtonDisabled"></v-btn>
|
|
</div>
|
|
<v-badge location="top right" color="#ff3300" dot class="cc-badge" :model-value="showRedDotBadge">
|
|
<v-btn class="info-button clickable" icon="$vuetify.icons.ic_share_settings" size="44" elevation="0"
|
|
color="black" @click.stop="showInformation" :disabled="currentAttachment?.status !== 'loaded'"></v-btn>
|
|
</v-badge>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- ATTACHMENT SENDING/SENT MODE -->
|
|
<template v-if="batch.attachments.length > 0 && (status == mainStatuses.SENDING || status == mainStatuses.SENT)">
|
|
<div class="file-drop-sent-stack" ref="stackContainer">
|
|
<div v-if="status == mainStatuses.SENDING && batch.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 batch.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 batch.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="batch.cancelSendAttachment(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",
|
|
batch.attachmentsSent.length
|
|
)
|
|
}}
|
|
</div>
|
|
<div class="file-drop-section">
|
|
<v-textarea disabled full-width variant="solo" flat 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="text" @click.stop="reset">{{ $t("file_mode.send_more_files") }}</v-btn>
|
|
<v-btn class="close" @click.stop="close">{{ $t("file_mode.close") }}</v-btn>
|
|
</div>
|
|
</template>
|
|
|
|
<v-bottom-sheet v-model="showAttachmentInformation" theme="dark" height="80%">
|
|
<v-card class="text-center send-attachments-info-popup">
|
|
<v-card-title class="d-flex flex-column pa-0">
|
|
<div class="align-self-end done-button clickable" @click="showAttachmentInformation = false">{{
|
|
$t("menu.done") }}
|
|
</div>
|
|
<v-divider />
|
|
</v-card-title>
|
|
<v-card-title class="d-flex">
|
|
<v-btn class="left-right-arrow flex-0-0" icon="chevron_left" @click.stop="currentItemIndex -= 1"
|
|
:disabled="currentItemIndex == 0"></v-btn>
|
|
<div class="title flex-fill">{{ currentAttachment.file.name }}</div>
|
|
<v-btn class="left-right-arrow flex-0-0" icon="chevron_right" @click.stop="currentItemIndex += 1"
|
|
:disabled="currentItemIndex >= batch.attachments.length - 1"></v-btn>
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<AttachmentInfo :attachment="currentAttachment" />
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-bottom-sheet>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, reactive } from "vue";
|
|
import messageMixin from "../messages/messageMixin";
|
|
import { Attachment } from "../../models/attachment";
|
|
import { createUploadBatch } from "../../models/attachmentManager";
|
|
import AttachmentInfo from "./AttachmentInfo.vue";
|
|
import ThumbnailView from "./ThumbnailView.vue";
|
|
|
|
export default defineComponent({
|
|
mixins: [messageMixin],
|
|
components: { AttachmentInfo, ThumbnailView },
|
|
emits: ["pick-file", "close"],
|
|
props: {
|
|
defaultRootMessageText: {
|
|
type: String,
|
|
default: function () {
|
|
return "";
|
|
},
|
|
},
|
|
|
|
fileModeMode: {
|
|
type: Boolean,
|
|
default: function () {
|
|
return false;
|
|
},
|
|
},
|
|
|
|
title: {
|
|
type: String,
|
|
default: function () {
|
|
return "";
|
|
},
|
|
},
|
|
|
|
batch: {
|
|
type: Object,
|
|
default: () => {
|
|
return createUploadBatch(null, null);
|
|
},
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
currentItemIndex: -1,
|
|
messageInput: "",
|
|
mainStatuses: Object.freeze({
|
|
SELECTING: 0,
|
|
SENDING: 1,
|
|
SENT: 2,
|
|
}),
|
|
status: 0,
|
|
dropTarget: false,
|
|
showAttachmentInformation: false,
|
|
};
|
|
},
|
|
mounted() {
|
|
this.$audioPlayer.setAutoplay(false);
|
|
},
|
|
computed: {
|
|
backButtonDisabled() {
|
|
return this.status == this.mainStatuses.SENDING;
|
|
},
|
|
sendButtonDisabled() {
|
|
return !this.batch.canSend;
|
|
},
|
|
currentAttachment(): Attachment | undefined {
|
|
if (this.currentItemIndex < 0 && this.batch.attachments.length > 0) {
|
|
this.currentItemIndex = this.batch.attachments.length - 1;
|
|
}
|
|
if (this.currentItemIndex >= 0 && this.currentItemIndex < this.batch.attachments.length) {
|
|
return this.batch.attachments[this.currentItemIndex];
|
|
}
|
|
return undefined;
|
|
},
|
|
showRedDotBadge(): boolean {
|
|
return this.currentAttachment && this.currentAttachment.proof?.integrity?.c2pa !== undefined && !this.currentAttachment.detailsViewed;
|
|
},
|
|
showCCIcon(): boolean {
|
|
return this.currentAttachment && this.currentAttachment.proof?.integrity?.c2pa !== undefined && !this.currentAttachment.useScaled;
|
|
},
|
|
currentlyViewedItem(): Attachment | undefined {
|
|
if (this.showAttachmentInformation) {
|
|
return this.currentAttachment;
|
|
}
|
|
return undefined;
|
|
}
|
|
},
|
|
watch: {
|
|
"batch.attachments": {
|
|
handler(newValue, oldValue) {
|
|
// Added or removed?
|
|
if (newValue && oldValue && newValue.length > oldValue.length) {
|
|
this.currentItemIndex = oldValue.length;
|
|
} else if (newValue) {
|
|
this.currentItemIndex = newValue.length - 1;
|
|
}
|
|
},
|
|
deep: 1,
|
|
},
|
|
currentlyViewedItem(newVal) {
|
|
if (newVal) {
|
|
newVal.detailsViewed = true; // We have seen this now
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
showInformation() {
|
|
if (this.currentAttachment) {
|
|
this.showAttachmentInformation = true;
|
|
}
|
|
},
|
|
filesDropped(e: DragEvent) {
|
|
this.dropTarget = false;
|
|
let droppedFiles: FileList | undefined = e.dataTransfer?.files;
|
|
if (!droppedFiles) return;
|
|
let files: File[] = [];
|
|
for (let i = 0; i < droppedFiles.length; i++) {
|
|
const file = droppedFiles.item(i);
|
|
if (file) files.push(file);
|
|
}
|
|
this.batch.addFiles(files);
|
|
},
|
|
reset() {
|
|
this.sendingAttachments = [];
|
|
this.status = this.mainStatuses.SELECTING;
|
|
this.messageInput = "";
|
|
this.currentItemIndex = 0;
|
|
this.$emit('reset');
|
|
},
|
|
close() {
|
|
this.batch.cancel();
|
|
this.status = this.mainStatuses.SELECTING;
|
|
this.messageInput = "";
|
|
this.currentItemIndex = -1;
|
|
this.$emit("close");
|
|
},
|
|
sendAll() {
|
|
this.status = this.mainStatuses.SENDING;
|
|
if (!this.fileModeMode) {
|
|
this.$emit("close");
|
|
}
|
|
this.batch
|
|
.send(this.messageInput && this.messageInput.length > 0 ? this.messageInput : this.defaultRootMessageText)
|
|
.then(() => {
|
|
this.status = this.mainStatuses.SENT;
|
|
});
|
|
},
|
|
stackItemTransform(attachment: Attachment, index: number) {
|
|
const size =
|
|
0.6 *
|
|
(this.$refs.stackContainer
|
|
? Math.min(this.$refs.stackContainer.clientWidth, this.$refs.stackContainer.clientHeight)
|
|
: 176);
|
|
let transform = "";
|
|
if (attachment != null && attachment.sendInfo && index != -1) {
|
|
transform =
|
|
"transform: rotate(" +
|
|
attachment.sendInfo.randomRotation +
|
|
"deg) translate(" +
|
|
attachment.sendInfo.randomTranslationX +
|
|
"px," +
|
|
attachment.sendInfo.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 *;
|
|
|
|
.cc-badge.v-badge--dot .v-badge__badge {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 4px;
|
|
}
|
|
</style>
|