mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 02:32:29 +00:00
feat: Adds image attachment for help center articles (#6426)
* Added one more endpoint to attach tempfile and get logo * Added one more endpoint to attach tempfile and get logo * spec fixes * Upload file for articles irrespective of the association * Upload file for articles irrespective of the association * Add multiple images with different keys * feat: Adds image attachment for help center articles * Adds validation for file upload * Fixes space above image after adding to doc * chore: Removed svg from file upload type * Update app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> * Update app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> * Removes caption for the image * Fixes woot prosemirror package version * Update yarn.lock * Update yarn.lock --------- Co-authored-by: Tejaswini Chile <tejaswini@chatwoot.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
committed by
GitHub
parent
80784e3cab
commit
2c8ecbeceb
@@ -46,6 +46,20 @@ class ArticlesAPI extends PortalsAPI {
|
||||
deleteArticle({ articleId, portalSlug }) {
|
||||
return axios.delete(`${this.url}/${portalSlug}/articles/${articleId}`);
|
||||
}
|
||||
|
||||
uploadImage({ portalSlug, file }) {
|
||||
let formData = new FormData();
|
||||
formData.append('background_image', file);
|
||||
return axios.post(
|
||||
`${this.url}/${portalSlug}/articles/attach_file`,
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default new ArticlesAPI();
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="editor-root editor--article">
|
||||
<input
|
||||
ref="imageUploadInput"
|
||||
type="file"
|
||||
accept="image/png, image/jpeg, image/jpg, image/gif, image/webp"
|
||||
hidden
|
||||
@change="onFileChange"
|
||||
/>
|
||||
<div ref="editor" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,23 +23,31 @@ import {
|
||||
EditorState,
|
||||
Selection,
|
||||
} from '@chatwoot/prosemirror-schema';
|
||||
|
||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
|
||||
const createState = (content, placeholder, plugins = []) => {
|
||||
const MAXIMUM_FILE_UPLOAD_SIZE = 4; // in MB
|
||||
const createState = (
|
||||
content,
|
||||
placeholder,
|
||||
plugins = [],
|
||||
onImageUpload = () => {}
|
||||
) => {
|
||||
return EditorState.create({
|
||||
doc: new ArticleMarkdownTransformer(fullSchema).parse(content),
|
||||
plugins: wootArticleWriterSetup({
|
||||
schema: fullSchema,
|
||||
placeholder,
|
||||
plugins,
|
||||
onImageUpload,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
mixins: [eventListenerMixins, uiSettingsMixin],
|
||||
mixins: [eventListenerMixins, uiSettingsMixin, alertMixin],
|
||||
props: {
|
||||
value: { type: String, default: '' },
|
||||
editorId: { type: String, default: '' },
|
||||
@@ -64,7 +79,12 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.state = createState(this.value, this.placeholder, this.plugins);
|
||||
this.state = createState(
|
||||
this.value,
|
||||
this.placeholder,
|
||||
this.plugins,
|
||||
this.openFileBrowser
|
||||
);
|
||||
},
|
||||
mounted() {
|
||||
this.createEditorView();
|
||||
@@ -73,8 +93,67 @@ export default {
|
||||
this.focusEditorInputField();
|
||||
},
|
||||
methods: {
|
||||
openFileBrowser() {
|
||||
this.$refs.imageUploadInput.click();
|
||||
},
|
||||
onFileChange() {
|
||||
const file = this.$refs.imageUploadInput.files[0];
|
||||
|
||||
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
||||
this.uploadImageToStorage(file);
|
||||
} else {
|
||||
this.showAlert(
|
||||
this.$t('HELP_CENTER.ARTICLE_EDITOR.IMAGE_UPLOAD.ERROR_FILE_SIZE', {
|
||||
size: MAXIMUM_FILE_UPLOAD_SIZE,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.$refs.imageUploadInput.value = '';
|
||||
},
|
||||
async uploadImageToStorage(file) {
|
||||
try {
|
||||
const fileUrl = await this.$store.dispatch('articles/attachImage', {
|
||||
portalSlug: this.$route.params.portalSlug,
|
||||
file,
|
||||
});
|
||||
|
||||
if (fileUrl) {
|
||||
this.onImageUploadStart(fileUrl);
|
||||
}
|
||||
this.showAlert(
|
||||
this.$t('HELP_CENTER.ARTICLE_EDITOR.IMAGE_UPLOAD.SUCCESS')
|
||||
);
|
||||
} catch (error) {
|
||||
this.showAlert(
|
||||
this.$t('HELP_CENTER.ARTICLE_EDITOR.IMAGE_UPLOAD.ERROR')
|
||||
);
|
||||
}
|
||||
},
|
||||
onImageUploadStart(fileUrl) {
|
||||
const { selection } = this.editorView.state;
|
||||
const from = selection.from;
|
||||
const node = this.editorView.state.schema.nodes.image.create({
|
||||
src: fileUrl,
|
||||
});
|
||||
const paragraphNode = this.editorView.state.schema.node('paragraph');
|
||||
if (node) {
|
||||
// Insert the image and the caption wrapped inside a paragraph
|
||||
const tr = this.editorView.state.tr
|
||||
.replaceSelectionWith(paragraphNode)
|
||||
.insert(from + 1, node);
|
||||
|
||||
this.editorView.dispatch(tr.scrollIntoView());
|
||||
this.focusEditorInputField();
|
||||
}
|
||||
},
|
||||
reloadState() {
|
||||
this.state = createState(this.value, this.placeholder, this.plugins);
|
||||
this.state = createState(
|
||||
this.value,
|
||||
this.placeholder,
|
||||
this.plugins,
|
||||
this.openFileBrowser
|
||||
);
|
||||
this.editorView.updateState(this.state);
|
||||
this.focusEditorInputField();
|
||||
},
|
||||
|
||||
@@ -28,6 +28,17 @@
|
||||
"SAVING": "Saving...",
|
||||
"SAVED": "Saved"
|
||||
},
|
||||
"ARTICLE_EDITOR": {
|
||||
"IMAGE_UPLOAD": {
|
||||
"TITLE": "Upload image",
|
||||
"UPLOADING": "Uploading...",
|
||||
"SUCCESS": "Image uploaded successfully",
|
||||
"ERROR": "Error while uploading image",
|
||||
"ERROR_FILE_SIZE": "Image size should be less than {size}MB",
|
||||
"ERROR_FILE_FORMAT": "Image format should be jpg, jpeg or png",
|
||||
"ERROR_FILE_DIMENSIONS": "Image dimensions should be less than 2000 x 2000"
|
||||
}
|
||||
},
|
||||
"ARTICLE_SETTINGS": {
|
||||
"TITLE": "Article Settings",
|
||||
"FORM": {
|
||||
|
||||
@@ -123,4 +123,19 @@ export const actions = {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
attachImage: async (_, { portalSlug, file }) => {
|
||||
try {
|
||||
const {
|
||||
data: { file_url: fileUrl },
|
||||
} = await articlesAPI.uploadImage({
|
||||
portalSlug,
|
||||
file,
|
||||
});
|
||||
return fileUrl;
|
||||
} catch (error) {
|
||||
throwErrorMessage(error);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braid/vue-formulate": "^2.5.2",
|
||||
"@chatwoot/prosemirror-schema": "https://github.com/chatwoot/prosemirror-schema.git#variable-mention",
|
||||
"@chatwoot/prosemirror-schema": "https://github.com/chatwoot/prosemirror-schema.git",
|
||||
"@chatwoot/utils": "^0.0.10",
|
||||
"@hcaptcha/vue-hcaptcha": "^0.3.2",
|
||||
"@june-so/analytics-next": "^1.36.5",
|
||||
|
||||
@@ -1391,9 +1391,9 @@
|
||||
is-url "^1.2.4"
|
||||
nanoid "^2.1.11"
|
||||
|
||||
"@chatwoot/prosemirror-schema@https://github.com/chatwoot/prosemirror-schema.git#variable-mention":
|
||||
"@chatwoot/prosemirror-schema@https://github.com/chatwoot/prosemirror-schema.git":
|
||||
version "1.0.0"
|
||||
resolved "https://github.com/chatwoot/prosemirror-schema.git#2205f322e54517c415d54b013742838f2e5faf89"
|
||||
resolved "https://github.com/chatwoot/prosemirror-schema.git#ebbf09d6ebd9138cdf2bb47257bb02ebbf01af53"
|
||||
dependencies:
|
||||
prosemirror-commands "^1.1.4"
|
||||
prosemirror-dropcursor "^1.3.2"
|
||||
|
||||
Reference in New Issue
Block a user