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:
Nithin David Thomas
2023-02-16 10:41:32 +05:30
committed by GitHub
parent 80784e3cab
commit 2c8ecbeceb
6 changed files with 127 additions and 8 deletions

View File

@@ -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();

View File

@@ -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();
},

View File

@@ -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": {

View File

@@ -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 '';
},
};

View File

@@ -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",

View File

@@ -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"