mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-03 04:27:53 +00:00
feat: Activestorage direct upload (#3768)
This commit is contained in:
@@ -31,7 +31,6 @@ class Messages::MessageBuilder
|
|||||||
@attachments.each do |uploaded_attachment|
|
@attachments.each do |uploaded_attachment|
|
||||||
@message.attachments.build(
|
@message.attachments.build(
|
||||||
account_id: @message.account_id,
|
account_id: @message.account_id,
|
||||||
file_type: file_type(uploaded_attachment&.content_type),
|
|
||||||
file: uploaded_attachment
|
file: uploaded_attachment
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
|
|||||||
params[:message][:attachments].each do |uploaded_attachment|
|
params[:message][:attachments].each do |uploaded_attachment|
|
||||||
@message.attachments.new(
|
@message.attachments.new(
|
||||||
account_id: @message.account_id,
|
account_id: @message.account_id,
|
||||||
file_type: helpers.file_type(uploaded_attachment&.content_type),
|
|
||||||
file: uploaded_attachment
|
file: uploaded_attachment
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ export const buildCreatePayload = ({
|
|||||||
let payload;
|
let payload;
|
||||||
if (files && files.length !== 0) {
|
if (files && files.length !== 0) {
|
||||||
payload = new FormData();
|
payload = new FormData();
|
||||||
files.forEach(file => {
|
|
||||||
payload.append('attachments[]', file, file.name);
|
|
||||||
});
|
|
||||||
if (message) {
|
if (message) {
|
||||||
payload.append('content', message);
|
payload.append('content', message);
|
||||||
}
|
}
|
||||||
|
files.forEach(file => {
|
||||||
|
payload.append('attachments[]', file);
|
||||||
|
});
|
||||||
payload.append('private', isPrivate);
|
payload.append('private', isPrivate);
|
||||||
payload.append('echo_id', echoId);
|
payload.append('echo_id', echoId);
|
||||||
payload.append('cc_emails', ccEmails);
|
payload.append('cc_emails', ccEmails);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
<div class="thumb-wrap">
|
<div class="thumb-wrap">
|
||||||
<img
|
<img
|
||||||
v-if="isTypeImage(attachment.resource.type)"
|
v-if="isTypeImage(attachment.resource.content_type)"
|
||||||
class="image-thumb"
|
class="image-thumb"
|
||||||
:src="attachment.thumb"
|
:src="attachment.thumb"
|
||||||
/>
|
/>
|
||||||
@@ -15,12 +15,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="file-name-wrap">
|
<div class="file-name-wrap">
|
||||||
<span class="item">
|
<span class="item">
|
||||||
{{ attachment.resource.name }}
|
{{ attachment.resource.filename }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="file-size-wrap">
|
<div class="file-size-wrap">
|
||||||
<span class="item">
|
<span class="item">
|
||||||
{{ formatFileSize(attachment.resource.size) }}
|
{{ formatFileSize(attachment.resource.byte_size) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="remove-file-wrap">
|
<div class="remove-file-wrap">
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
:multiple="enableMultipleFileUpload"
|
:multiple="enableMultipleFileUpload"
|
||||||
:drop="true"
|
:drop="true"
|
||||||
:drop-directory="false"
|
:drop-directory="false"
|
||||||
@input-file="onFileUpload"
|
:data="{ direct_upload_url: '/rails/active_storage/direct_uploads', direct_upload: true }"
|
||||||
|
@input-file="onDirectFileUpload"
|
||||||
>
|
>
|
||||||
<woot-button
|
<woot-button
|
||||||
v-if="showAttachButton"
|
v-if="showAttachButton"
|
||||||
@@ -80,6 +81,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import FileUpload from 'vue-upload-component';
|
import FileUpload from 'vue-upload-component';
|
||||||
|
import * as ActiveStorage from "activestorage";
|
||||||
import {
|
import {
|
||||||
hasPressedAltAndWKey,
|
hasPressedAltAndWKey,
|
||||||
hasPressedAltAndAKey,
|
hasPressedAltAndAKey,
|
||||||
@@ -109,7 +111,7 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
onFileUpload: {
|
onDirectFileUpload: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
@@ -150,6 +152,9 @@ export default {
|
|||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
ActiveStorage.start();
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isNote() {
|
isNote() {
|
||||||
return this.mode === REPLY_EDITOR_MODES.NOTE;
|
return this.mode === REPLY_EDITOR_MODES.NOTE;
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
<reply-bottom-panel
|
<reply-bottom-panel
|
||||||
:mode="replyType"
|
:mode="replyType"
|
||||||
:send-button-text="replyButtonLabel"
|
:send-button-text="replyButtonLabel"
|
||||||
:on-file-upload="onFileUpload"
|
:on-direct-file-upload="onDirectFileUpload"
|
||||||
:show-file-upload="showFileUpload"
|
:show-file-upload="showFileUpload"
|
||||||
:toggle-emoji-picker="toggleEmojiPicker"
|
:toggle-emoji-picker="toggleEmojiPicker"
|
||||||
:show-emoji-picker="showEmojiPicker"
|
:show-emoji-picker="showEmojiPicker"
|
||||||
@@ -104,6 +104,7 @@ import {
|
|||||||
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
||||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||||
|
import { DirectUpload } from 'activestorage';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -443,6 +444,35 @@ export default {
|
|||||||
isPrivate,
|
isPrivate,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
onDirectFileUpload(file) {
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
||||||
|
const upload = new DirectUpload(file.file, '/rails/active_storage/direct_uploads', null, file.file.name);
|
||||||
|
upload.create((error, blob) => {
|
||||||
|
if (error) {
|
||||||
|
this.showAlert(
|
||||||
|
error
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.attachedFiles.push({
|
||||||
|
currentChatId: this.currentChat.id,
|
||||||
|
resource: blob,
|
||||||
|
isPrivate: this.isPrivate,
|
||||||
|
thumb: null,
|
||||||
|
blobSignedId: blob.signed_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.showAlert(
|
||||||
|
this.$t('CONVERSATION.FILE_SIZE_LIMIT', {
|
||||||
|
MAXIMUM_FILE_UPLOAD_SIZE,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
onFileUpload(file) {
|
onFileUpload(file) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
@@ -486,7 +516,7 @@ export default {
|
|||||||
if (this.attachedFiles && this.attachedFiles.length) {
|
if (this.attachedFiles && this.attachedFiles.length) {
|
||||||
messagePayload.files = [];
|
messagePayload.files = [];
|
||||||
this.attachedFiles.forEach(attachment => {
|
this.attachedFiles.forEach(attachment => {
|
||||||
messagePayload.files.push(attachment.resource.file);
|
messagePayload.files.push(attachment.blobSignedId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ const sendAttachment = ({ attachment }) => {
|
|||||||
const { file } = attachment;
|
const { file } = attachment;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('message[attachments][]', file, file.name);
|
formData.append('message[attachments][]', file);
|
||||||
formData.append('message[referer_url]', referrerURL);
|
formData.append('message[referer_url]', referrerURL);
|
||||||
formData.append('message[timestamp]', timestamp);
|
formData.append('message[timestamp]', timestamp);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
<file-upload
|
<file-upload
|
||||||
:size="4096 * 2048"
|
:size="4096 * 2048"
|
||||||
:accept="allowedFileTypes"
|
:accept="allowedFileTypes"
|
||||||
@input-file="onFileUpload"
|
:data="{ direct_upload_url: '', direct_upload: true }"
|
||||||
|
@input-file="onDirectFileUpload"
|
||||||
>
|
>
|
||||||
<button class="icon-button flex items-center justify-center">
|
<button class="icon-button flex items-center justify-center">
|
||||||
<fluent-icon v-if="!isUploading.image" icon="attach" />
|
<fluent-icon v-if="!isUploading.image" icon="attach" />
|
||||||
@@ -21,6 +22,8 @@ import {
|
|||||||
} from 'shared/constants/messages';
|
} from 'shared/constants/messages';
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
||||||
|
import { DirectUpload } from 'activestorage';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { FluentIcon, FileUpload, Spinner },
|
components: { FluentIcon, FileUpload, Spinner },
|
||||||
props: {
|
props: {
|
||||||
@@ -44,6 +47,39 @@ export default {
|
|||||||
getFileType(fileType) {
|
getFileType(fileType) {
|
||||||
return fileType.includes('image') ? 'image' : 'file';
|
return fileType.includes('image') ? 'image' : 'file';
|
||||||
},
|
},
|
||||||
|
async onDirectFileUpload(file) {
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isUploading = true;
|
||||||
|
try {
|
||||||
|
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
||||||
|
const upload = new DirectUpload(file.file, '/rails/active_storage/direct_uploads', null, file.file.name);
|
||||||
|
|
||||||
|
upload.create((error, blob) => {
|
||||||
|
if (error) {
|
||||||
|
window.bus.$emit(BUS_EVENTS.SHOW_ALERT, {
|
||||||
|
message: error,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.onAttach({
|
||||||
|
fileType: blob.content_type,
|
||||||
|
file: blob.signed_id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.bus.$emit(BUS_EVENTS.SHOW_ALERT, {
|
||||||
|
message: this.$t('FILE_SIZE_LIMIT', {
|
||||||
|
MAXIMUM_FILE_UPLOAD_SIZE: this.fileUploadSizeLimit,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Error
|
||||||
|
}
|
||||||
|
this.isUploading = false;
|
||||||
|
},
|
||||||
async onFileUpload(file) {
|
async onFileUpload(file) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"@rails/webpacker": "5.3.0",
|
"@rails/webpacker": "5.3.0",
|
||||||
"@sentry/tracing": "^6.4.1",
|
"@sentry/tracing": "^6.4.1",
|
||||||
"@sentry/vue": "^6.4.1",
|
"@sentry/vue": "^6.4.1",
|
||||||
|
"activestorage": "^5.2.6",
|
||||||
"axios": "^0.21.2",
|
"axios": "^0.21.2",
|
||||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||||
"babel-plugin-transform-vue-jsx": "^3.7.0",
|
"babel-plugin-transform-vue-jsx": "^3.7.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user