mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 20:18:08 +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 }) {
|
deleteArticle({ articleId, portalSlug }) {
|
||||||
return axios.delete(`${this.url}/${portalSlug}/articles/${articleId}`);
|
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();
|
export default new ArticlesAPI();
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="editor-root editor--article">
|
<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 ref="editor" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -16,23 +23,31 @@ import {
|
|||||||
EditorState,
|
EditorState,
|
||||||
Selection,
|
Selection,
|
||||||
} from '@chatwoot/prosemirror-schema';
|
} from '@chatwoot/prosemirror-schema';
|
||||||
|
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||||
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
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({
|
return EditorState.create({
|
||||||
doc: new ArticleMarkdownTransformer(fullSchema).parse(content),
|
doc: new ArticleMarkdownTransformer(fullSchema).parse(content),
|
||||||
plugins: wootArticleWriterSetup({
|
plugins: wootArticleWriterSetup({
|
||||||
schema: fullSchema,
|
schema: fullSchema,
|
||||||
placeholder,
|
placeholder,
|
||||||
plugins,
|
plugins,
|
||||||
|
onImageUpload,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [eventListenerMixins, uiSettingsMixin],
|
mixins: [eventListenerMixins, uiSettingsMixin, alertMixin],
|
||||||
props: {
|
props: {
|
||||||
value: { type: String, default: '' },
|
value: { type: String, default: '' },
|
||||||
editorId: { type: String, default: '' },
|
editorId: { type: String, default: '' },
|
||||||
@@ -64,7 +79,12 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.state = createState(this.value, this.placeholder, this.plugins);
|
this.state = createState(
|
||||||
|
this.value,
|
||||||
|
this.placeholder,
|
||||||
|
this.plugins,
|
||||||
|
this.openFileBrowser
|
||||||
|
);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.createEditorView();
|
this.createEditorView();
|
||||||
@@ -73,8 +93,67 @@ export default {
|
|||||||
this.focusEditorInputField();
|
this.focusEditorInputField();
|
||||||
},
|
},
|
||||||
methods: {
|
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() {
|
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.editorView.updateState(this.state);
|
||||||
this.focusEditorInputField();
|
this.focusEditorInputField();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,6 +28,17 @@
|
|||||||
"SAVING": "Saving...",
|
"SAVING": "Saving...",
|
||||||
"SAVED": "Saved"
|
"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": {
|
"ARTICLE_SETTINGS": {
|
||||||
"TITLE": "Article Settings",
|
"TITLE": "Article Settings",
|
||||||
"FORM": {
|
"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": {
|
"dependencies": {
|
||||||
"@braid/vue-formulate": "^2.5.2",
|
"@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",
|
"@chatwoot/utils": "^0.0.10",
|
||||||
"@hcaptcha/vue-hcaptcha": "^0.3.2",
|
"@hcaptcha/vue-hcaptcha": "^0.3.2",
|
||||||
"@june-so/analytics-next": "^1.36.5",
|
"@june-so/analytics-next": "^1.36.5",
|
||||||
|
|||||||
@@ -1391,9 +1391,9 @@
|
|||||||
is-url "^1.2.4"
|
is-url "^1.2.4"
|
||||||
nanoid "^2.1.11"
|
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"
|
version "1.0.0"
|
||||||
resolved "https://github.com/chatwoot/prosemirror-schema.git#2205f322e54517c415d54b013742838f2e5faf89"
|
resolved "https://github.com/chatwoot/prosemirror-schema.git#ebbf09d6ebd9138cdf2bb47257bb02ebbf01af53"
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-commands "^1.1.4"
|
prosemirror-commands "^1.1.4"
|
||||||
prosemirror-dropcursor "^1.3.2"
|
prosemirror-dropcursor "^1.3.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user