diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 15:26:25 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 15:26:25 +0300 |
commit | a09983ae35713f5a2bbb100981116d31ce99826e (patch) | |
tree | 2ee2af7bd104d57086db360a7e6d8c9d5d43667a /app/assets/javascripts/static_site_editor | |
parent | 18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff) |
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'app/assets/javascripts/static_site_editor')
12 files changed, 124 insertions, 49 deletions
diff --git a/app/assets/javascripts/static_site_editor/components/edit_area.vue b/app/assets/javascripts/static_site_editor/components/edit_area.vue index e9efef40632..84a16f327d9 100644 --- a/app/assets/javascripts/static_site_editor/components/edit_area.vue +++ b/app/assets/javascripts/static_site_editor/components/edit_area.vue @@ -5,6 +5,8 @@ import EditHeader from './edit_header.vue'; import UnsavedChangesConfirmDialog from './unsaved_changes_confirm_dialog.vue'; import parseSourceFile from '~/static_site_editor/services/parse_source_file'; import { EDITOR_TYPES } from '~/vue_shared/components/rich_content_editor/constants'; +import { DEFAULT_IMAGE_UPLOAD_PATH } from '../constants'; +import imageRepository from '../image_repository'; export default { components: { @@ -31,46 +33,47 @@ export default { required: false, default: '', }, + imageRoot: { + type: String, + required: false, + default: DEFAULT_IMAGE_UPLOAD_PATH, + validator: prop => prop.endsWith('/'), + }, }, data() { return { saveable: false, parsedSource: parseSourceFile(this.content), editorMode: EDITOR_TYPES.wysiwyg, + isModified: false, }; }, + imageRepository: imageRepository(), computed: { editableContent() { - return this.parsedSource.editable; - }, - editableKey() { - return this.isWysiwygMode ? 'body' : 'raw'; + return this.parsedSource.content(this.isWysiwygMode); }, isWysiwygMode() { return this.editorMode === EDITOR_TYPES.wysiwyg; }, - modified() { - return this.isWysiwygMode - ? this.parsedSource.isModifiedBody() - : this.parsedSource.isModifiedRaw(); - }, }, methods: { - syncSource() { - if (this.isWysiwygMode) { - this.parsedSource.syncBody(); - return; - } - - this.parsedSource.syncRaw(); + onInputChange(newVal) { + this.parsedSource.sync(newVal, this.isWysiwygMode); + this.isModified = this.parsedSource.isModified(); }, onModeChange(mode) { this.editorMode = mode; - this.syncSource(); + this.$refs.editor.resetInitialValue(this.editableContent); + }, + onUploadImage({ file, imageUrl }) { + this.$options.imageRepository.add(file, imageUrl); }, onSubmit() { - this.syncSource(); - this.$emit('submit', { content: this.editableContent.raw }); + this.$emit('submit', { + content: this.parsedSource.content(), + images: this.$options.imageRepository.getAll(), + }); }, }, }; @@ -79,16 +82,20 @@ export default { <div class="d-flex flex-grow-1 flex-column h-100"> <edit-header class="py-2" :title="title" /> <rich-content-editor - v-model="editableContent[editableKey]" + ref="editor" + :content="editableContent" :initial-edit-type="editorMode" + :image-root="imageRoot" class="mb-9 h-100" @modeChange="onModeChange" + @input="onInputChange" + @uploadImage="onUploadImage" /> - <unsaved-changes-confirm-dialog :modified="modified" /> + <unsaved-changes-confirm-dialog :modified="isModified" /> <publish-toolbar class="gl-fixed gl-left-0 gl-bottom-0 gl-w-full" :return-url="returnUrl" - :saveable="modified" + :saveable="isModified" :saving-changes="savingChanges" @submit="onSubmit" /> diff --git a/app/assets/javascripts/static_site_editor/constants.js b/app/assets/javascripts/static_site_editor/constants.js index 947347922f2..49db9ab7ca5 100644 --- a/app/assets/javascripts/static_site_editor/constants.js +++ b/app/assets/javascripts/static_site_editor/constants.js @@ -19,3 +19,5 @@ export const DEFAULT_HEADING = s__('StaticSiteEditor|Static site editor'); export const TRACKING_ACTION_CREATE_COMMIT = 'create_commit'; export const TRACKING_ACTION_CREATE_MERGE_REQUEST = 'create_merge_request'; export const TRACKING_ACTION_INITIALIZE_EDITOR = 'initialize_editor'; + +export const DEFAULT_IMAGE_UPLOAD_PATH = 'source/images/uploads/'; diff --git a/app/assets/javascripts/static_site_editor/graphql/mutations/submit_content_changes.mutation.graphql b/app/assets/javascripts/static_site_editor/graphql/mutations/submit_content_changes.mutation.graphql index 2840d419966..cd130aa7dbb 100644 --- a/app/assets/javascripts/static_site_editor/graphql/mutations/submit_content_changes.mutation.graphql +++ b/app/assets/javascripts/static_site_editor/graphql/mutations/submit_content_changes.mutation.graphql @@ -1,5 +1,5 @@ mutation submitContentChanges($input: SubmitContentChangesInput) { - submitContentChanges(input: $input) @client { + submitContentChanges(input: $input) @client { branch commit mergeRequest diff --git a/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql b/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql index fdbf4459aee..946d80efff0 100644 --- a/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql +++ b/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql @@ -3,7 +3,7 @@ query appData { isSupportedContent project sourcePath - username, + username returnUrl } } diff --git a/app/assets/javascripts/static_site_editor/graphql/queries/source_content.query.graphql b/app/assets/javascripts/static_site_editor/graphql/queries/source_content.query.graphql index e36d244ae57..cfe30c601ed 100644 --- a/app/assets/javascripts/static_site_editor/graphql/queries/source_content.query.graphql +++ b/app/assets/javascripts/static_site_editor/graphql/queries/source_content.query.graphql @@ -1,6 +1,6 @@ query sourceContent($project: ID!, $sourcePath: String!) { project(fullPath: $project) { - fullPath, + fullPath file(path: $sourcePath) @client { title content diff --git a/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js b/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js index 6c4e3a4d973..0cb26f88785 100644 --- a/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js +++ b/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js @@ -3,10 +3,10 @@ import savedContentMetaQuery from '../queries/saved_content_meta.query.graphql'; const submitContentChangesResolver = ( _, - { input: { project: projectId, username, sourcePath, content } }, + { input: { project: projectId, username, sourcePath, content, images } }, { cache }, ) => { - return submitContentChanges({ projectId, username, sourcePath, content }).then( + return submitContentChanges({ projectId, username, sourcePath, content, images }).then( savedContentMeta => { cache.writeQuery({ query: savedContentMetaQuery, diff --git a/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql b/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql index 59da2e27144..78cc1746cdb 100644 --- a/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql +++ b/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql @@ -22,7 +22,7 @@ type AppData { username: String! } -type SubmitContentChangesInput { +input SubmitContentChangesInput { project: String! sourcePath: String! content: String! diff --git a/app/assets/javascripts/static_site_editor/image_repository.js b/app/assets/javascripts/static_site_editor/image_repository.js new file mode 100644 index 00000000000..541d581bda8 --- /dev/null +++ b/app/assets/javascripts/static_site_editor/image_repository.js @@ -0,0 +1,20 @@ +import { __ } from '~/locale'; +import Flash from '~/flash'; +import { getBinary } from './services/image_service'; + +const imageRepository = () => { + const images = new Map(); + const flash = message => new Flash(message); + + const add = (file, url) => { + getBinary(file) + .then(content => images.set(url, content)) + .catch(() => flash(__('Something went wrong while inserting your image. Please try again.'))); + }; + + const getAll = () => images; + + return { add, getAll }; +}; + +export default imageRepository; diff --git a/app/assets/javascripts/static_site_editor/pages/home.vue b/app/assets/javascripts/static_site_editor/pages/home.vue index a1314c8a478..156b815e07a 100644 --- a/app/assets/javascripts/static_site_editor/pages/home.vue +++ b/app/assets/javascripts/static_site_editor/pages/home.vue @@ -67,11 +67,11 @@ export default { onDismissError() { this.submitChangesError = null; }, - onSubmit({ content }) { + onSubmit({ content, images }) { this.content = content; - this.submitChanges(); + this.submitChanges(images); }, - submitChanges() { + submitChanges(images) { this.isSavingChanges = true; this.$apollo @@ -83,6 +83,7 @@ export default { username: this.appData.username, sourcePath: this.appData.sourcePath, content: this.content, + images, }, }, }) diff --git a/app/assets/javascripts/static_site_editor/services/image_service.js b/app/assets/javascripts/static_site_editor/services/image_service.js new file mode 100644 index 00000000000..edc69d0579a --- /dev/null +++ b/app/assets/javascripts/static_site_editor/services/image_service.js @@ -0,0 +1,9 @@ +// eslint-disable-next-line import/prefer-default-export +export const getBinary = file => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => resolve(reader.result.split(',')[1]); + reader.onerror = error => reject(error); + }); +}; diff --git a/app/assets/javascripts/static_site_editor/services/parse_source_file.js b/app/assets/javascripts/static_site_editor/services/parse_source_file.js index f32c693411f..126dfe81b90 100644 --- a/app/assets/javascripts/static_site_editor/services/parse_source_file.js +++ b/app/assets/javascripts/static_site_editor/services/parse_source_file.js @@ -22,33 +22,43 @@ const parseSourceFile = raw => { return buildPayload(source, '', '', source); }; - const computedRaw = () => `${editable.header}${editable.spacing}${editable.body}`; - - const syncBody = () => { + const syncEditable = () => { /* We re-parse as markdown editing could have added non-body changes (preFrontMatter, frontMatter, or spacing). - Re-parsing additionally gets us the desired body that was extracted from the mutated editable.raw - Additionally we intentionally mutate the existing editable's key values as opposed to reassigning the object itself so consumers of the potentially reactive property stay in sync. + Re-parsing additionally gets us the desired body that was extracted from the potentially mutated editable.raw */ - Object.assign(editable, parse(editable.raw)); + editable = parse(editable.raw); + }; + + const syncBodyToRaw = () => { + editable.raw = `${editable.header}${editable.spacing}${editable.body}`; + }; + + const sync = (newVal, isBodyToRaw) => { + const editableKey = isBodyToRaw ? 'body' : 'raw'; + editable[editableKey] = newVal; + + if (isBodyToRaw) { + syncBodyToRaw(); + } + + syncEditable(); }; - const syncRaw = () => { - editable.raw = computedRaw(); + const content = (isBody = false) => { + const editableKey = isBody ? 'body' : 'raw'; + return editable[editableKey]; }; - const isModifiedRaw = () => initial.raw !== editable.raw; - const isModifiedBody = () => initial.raw !== computedRaw(); + const isModified = () => initial.raw !== editable.raw; initial = parse(raw); editable = parse(raw); return { - editable, - isModifiedRaw, - isModifiedBody, - syncRaw, - syncBody, + content, + isModified, + sync, }; }; diff --git a/app/assets/javascripts/static_site_editor/services/submit_content_changes.js b/app/assets/javascripts/static_site_editor/services/submit_content_changes.js index fce7c1f918f..da62d3fa4fc 100644 --- a/app/assets/javascripts/static_site_editor/services/submit_content_changes.js +++ b/app/assets/javascripts/static_site_editor/services/submit_content_changes.js @@ -21,7 +21,32 @@ const createBranch = (projectId, branch) => throw new Error(SUBMIT_CHANGES_BRANCH_ERROR); }); -const commitContent = (projectId, message, branch, sourcePath, content) => { +const createImageActions = (images, markdown) => { + const actions = []; + + if (!markdown) { + return actions; + } + + images.forEach((imageContent, filePath) => { + const imageExistsInMarkdown = path => new RegExp(`!\\[([^[\\]\\n]*)\\](\\(${path})\\)`); // matches the image markdown syntax: ![<any-string-except-newline>](<path>) + + if (imageExistsInMarkdown(filePath).test(markdown)) { + actions.push( + convertObjectPropsToSnakeCase({ + encoding: 'base64', + action: 'create', + content: imageContent, + filePath, + }), + ); + } + }); + + return actions; +}; + +const commitContent = (projectId, message, branch, sourcePath, content, images) => { Tracking.event(document.body.dataset.page, TRACKING_ACTION_CREATE_COMMIT); return Api.commitMultiple( @@ -35,6 +60,7 @@ const commitContent = (projectId, message, branch, sourcePath, content) => { filePath: sourcePath, content, }), + ...createImageActions(images, content), ], }), ).catch(() => { @@ -62,7 +88,7 @@ const createMergeRequest = ( }); }; -const submitContentChanges = ({ username, projectId, sourcePath, content }) => { +const submitContentChanges = ({ username, projectId, sourcePath, content, images }) => { const branch = generateBranchName(username); const mergeRequestTitle = sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), { sourcePath, @@ -73,7 +99,7 @@ const submitContentChanges = ({ username, projectId, sourcePath, content }) => { .then(({ data: { web_url: url } }) => { Object.assign(meta, { branch: { label: branch, url } }); - return commitContent(projectId, mergeRequestTitle, branch, sourcePath, content); + return commitContent(projectId, mergeRequestTitle, branch, sourcePath, content, images); }) .then(({ data: { short_id: label, web_url: url } }) => { Object.assign(meta, { commit: { label, url } }); |