diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-21 02:50:22 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-21 02:50:22 +0300 |
commit | 9dc93a4519d9d5d7be48ff274127136236a3adb3 (patch) | |
tree | 70467ae3692a0e35e5ea56bcb803eb512a10bedb /app/assets/javascripts/repository | |
parent | 4b0f34b6d759d6299322b3a54453e930c6121ff0 (diff) |
Add latest changes from gitlab-org/gitlab@13-11-stable-eev13.11.0-rc43
Diffstat (limited to 'app/assets/javascripts/repository')
10 files changed, 286 insertions, 51 deletions
diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue new file mode 100644 index 00000000000..58b42fb7859 --- /dev/null +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -0,0 +1,100 @@ +<script> +import { GlLoadingIcon } from '@gitlab/ui'; +import { uniqueId } from 'lodash'; +import BlobContent from '~/blob/components/blob_content.vue'; +import BlobHeader from '~/blob/components/blob_header.vue'; +import createFlash from '~/flash'; +import { __ } from '~/locale'; +import blobInfoQuery from '../queries/blob_info.query.graphql'; +import projectPathQuery from '../queries/project_path.query.graphql'; + +export default { + components: { + BlobHeader, + BlobContent, + GlLoadingIcon, + }, + apollo: { + projectPath: { + query: projectPathQuery, + }, + blobInfo: { + query: blobInfoQuery, + variables() { + return { + projectPath: this.projectPath, + filePath: this.path, + }; + }, + error() { + createFlash({ message: __('An error occurred while loading the file. Please try again.') }); + }, + }, + }, + provide() { + return { + blobHash: uniqueId(), + }; + }, + props: { + path: { + type: String, + required: true, + }, + }, + data() { + return { + projectPath: '', + blobInfo: { + name: '', + size: '', + rawBlob: '', + type: '', + fileType: '', + tooLarge: false, + path: '', + editBlobPath: '', + ideEditPath: '', + storedExternally: false, + rawPath: '', + externalStorageUrl: '', + replacePath: '', + deletePath: '', + canLock: false, + isLocked: false, + lockLink: '', + canModifyBlob: true, + forkPath: '', + simpleViewer: '', + richViewer: '', + }, + }; + }, + computed: { + isLoading() { + return this.$apollo.queries.blobInfo.loading; + }, + viewer() { + const { fileType, tooLarge, type } = this.blobInfo; + + return { fileType, tooLarge, type }; + }, + }, +}; +</script> + +<template> + <div> + <gl-loading-icon v-if="isLoading" /> + <div v-if="blobInfo && !isLoading"> + <blob-header :blob="blobInfo" /> + <blob-content + :blob="blobInfo" + :content="blobInfo.rawBlob" + :is-raw-content="true" + :active-viewer="viewer" + :loading="false" + /> + </div> + </div> +</template> diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue index 28d7dec85f4..0b8408643ac 100644 --- a/app/assets/javascripts/repository/components/breadcrumbs.vue +++ b/app/assets/javascripts/repository/components/breadcrumbs.vue @@ -5,6 +5,7 @@ import { GlDropdownSectionHeader, GlDropdownItem, GlIcon, + GlModalDirective, } from '@gitlab/ui'; import permissionsQuery from 'shared_queries/repository/permissions.query.graphql'; import { joinPaths, escapeFileUrl } from '~/lib/utils/url_utility'; @@ -12,12 +13,15 @@ import { __ } from '../../locale'; import getRefMixin from '../mixins/get_ref'; import projectPathQuery from '../queries/project_path.query.graphql'; import projectShortPathQuery from '../queries/project_short_path.query.graphql'; +import UploadBlobModal from './upload_blob_modal.vue'; const ROW_TYPES = { header: 'header', divider: 'divider', }; +const UPLOAD_BLOB_MODAL_ID = 'modal-upload-blob'; + export default { components: { GlDropdown, @@ -25,6 +29,7 @@ export default { GlDropdownSectionHeader, GlDropdownItem, GlIcon, + UploadBlobModal, }, apollo: { projectShortPath: { @@ -46,6 +51,9 @@ export default { }, }, }, + directives: { + GlModal: GlModalDirective, + }, mixins: [getRefMixin], props: { currentPath: { @@ -63,6 +71,21 @@ export default { required: false, default: false, }, + canPushCode: { + type: Boolean, + required: false, + default: false, + }, + selectedBranch: { + type: String, + required: false, + default: '', + }, + originalBranch: { + type: String, + required: false, + default: '', + }, newBranchPath: { type: String, required: false, @@ -93,7 +116,13 @@ export default { required: false, default: null, }, + uploadPath: { + type: String, + required: false, + default: '', + }, }, + uploadBlobModalId: UPLOAD_BLOB_MODAL_ID, data() { return { projectShortPath: '', @@ -126,7 +155,10 @@ export default { ); }, canCreateMrFromFork() { - return this.userPermissions.forkProject && this.userPermissions.createMergeRequestIn; + return this.userPermissions?.forkProject && this.userPermissions?.createMergeRequestIn; + }, + showUploadModal() { + return this.canEditTree && !this.$apollo.queries.userPermissions.loading; }, dropdownItems() { const items = []; @@ -149,10 +181,9 @@ export default { { attrs: { href: '#modal-upload-blob', - 'data-target': '#modal-upload-blob', - 'data-toggle': 'modal', }, text: __('Upload file'), + modalId: UPLOAD_BLOB_MODAL_ID, }, { attrs: { @@ -253,12 +284,26 @@ export default { <gl-icon name="chevron-down" :size="16" class="float-left" /> </template> <template v-for="(item, i) in dropdownItems"> - <component :is="getComponent(item.type)" :key="i" v-bind="item.attrs"> + <component + :is="getComponent(item.type)" + :key="i" + v-bind="item.attrs" + v-gl-modal="item.modalId || null" + > {{ item.text }} </component> </template> </gl-dropdown> </li> </ol> + <upload-blob-modal + v-if="showUploadModal" + :modal-id="$options.uploadBlobModalId" + :commit-message="__('Upload New File')" + :target-branch="selectedBranch" + :original-branch="originalBranch" + :can-push-code="canPushCode" + :path="uploadPath" + /> </nav> </template> diff --git a/app/assets/javascripts/repository/components/directory_download_links.vue b/app/assets/javascripts/repository/components/directory_download_links.vue index 8c029fc9973..c222a83300d 100644 --- a/app/assets/javascripts/repository/components/directory_download_links.vue +++ b/app/assets/javascripts/repository/components/directory_download_links.vue @@ -1,9 +1,9 @@ <script> -import { GlLink } from '@gitlab/ui'; +import { GlButton } from '@gitlab/ui'; export default { components: { - GlLink, + GlButton, }, props: { currentPath: { @@ -32,15 +32,15 @@ export default { <h5 class="m-0 dropdown-bold-header">{{ __('Download this directory') }}</h5> <div class="dropdown-menu-content"> <div class="btn-group ml-0 w-100"> - <gl-link + <gl-button v-for="(link, index) in normalizedLinks" :key="index" :href="link.path" - :class="{ 'btn-primary': index === 0 }" - class="btn btn-xs" + :variant="index === 0 ? 'confirm' : 'default'" + size="small" > {{ link.text }} - </gl-link> + </gl-button> </div> </div> </section> diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue index 70918dd55e4..8ea5fce92fa 100644 --- a/app/assets/javascripts/repository/components/table/row.vue +++ b/app/assets/javascripts/repository/components/table/row.vue @@ -12,6 +12,7 @@ import { escapeRegExp } from 'lodash'; import { escapeFileUrl } from '~/lib/utils/url_utility'; import FileIcon from '~/vue_shared/components/file_icon.vue'; import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import getRefMixin from '../../mixins/get_ref'; import commitQuery from '../../queries/commit.query.graphql'; @@ -41,7 +42,7 @@ export default { }, }, }, - mixins: [getRefMixin], + mixins: [getRefMixin, glFeatureFlagMixin()], props: { id: { type: String, @@ -103,10 +104,21 @@ export default { }; }, computed: { + refactorBlobViewerEnabled() { + return this.glFeatures.refactorBlobViewer; + }, routerLinkTo() { - return this.isFolder - ? { path: `/-/tree/${this.escapedRef}/${escapeFileUrl(this.path)}` } - : null; + const blobRouteConfig = { path: `/-/blob/${this.escapedRef}/${escapeFileUrl(this.path)}` }; + const treeRouteConfig = { path: `/-/tree/${this.escapedRef}/${escapeFileUrl(this.path)}` }; + + if (this.refactorBlobViewerEnabled && this.isBlob) { + return blobRouteConfig; + } + + return this.isFolder ? treeRouteConfig : null; + }, + isBlob() { + return this.type === 'blob'; }, isFolder() { return this.type === 'tree'; @@ -115,7 +127,7 @@ export default { return this.type === 'commit'; }, linkComponent() { - return this.isFolder ? 'router-link' : 'a'; + return this.isFolder || (this.refactorBlobViewerEnabled && this.isBlob) ? 'router-link' : 'a'; }, fullPath() { return this.path.replace(new RegExp(`^${escapeRegExp(this.currentPath)}/`), ''); diff --git a/app/assets/javascripts/repository/components/tree_action_link.vue b/app/assets/javascripts/repository/components/tree_action_link.vue deleted file mode 100644 index c5ab150adaf..00000000000 --- a/app/assets/javascripts/repository/components/tree_action_link.vue +++ /dev/null @@ -1,28 +0,0 @@ -<script> -import { GlLink } from '@gitlab/ui'; - -export default { - components: { - GlLink, - }, - props: { - path: { - type: String, - required: true, - }, - text: { - type: String, - required: true, - }, - cssClass: { - type: String, - required: false, - default: null, - }, - }, -}; -</script> - -<template> - <gl-link :href="path" :class="cssClass" class="btn gl-button">{{ text }}</gl-link> -</template> diff --git a/app/assets/javascripts/repository/components/upload_blob_modal.vue b/app/assets/javascripts/repository/components/upload_blob_modal.vue index ec7ba469ca0..d2ff01e7fc1 100644 --- a/app/assets/javascripts/repository/components/upload_blob_modal.vue +++ b/app/assets/javascripts/repository/components/upload_blob_modal.vue @@ -168,6 +168,7 @@ export default { }); }, }, + validFileMimetypes: [], }; </script> <template> @@ -179,7 +180,12 @@ export default { :action-cancel="cancelOptions" @primary.prevent="uploadFile" > - <upload-dropzone class="gl-h-200! gl-mb-4" single-file-selection @change="setFile"> + <upload-dropzone + class="gl-h-200! gl-mb-4" + single-file-selection + :valid-file-mimetypes="$options.validFileMimetypes" + @change="setFile" + > <div v-if="file" class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3" diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js index e6969b7c8b2..3a9a2adb417 100644 --- a/app/assets/javascripts/repository/index.js +++ b/app/assets/javascripts/repository/index.js @@ -1,14 +1,18 @@ import { GlButton } from '@gitlab/ui'; import Vue from 'vue'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import { escapeFileUrl } from '~/lib/utils/url_utility'; +import { __ } from '~/locale'; import initWebIdeLink from '~/pages/projects/shared/web_ide_link'; -import { parseBoolean } from '../lib/utils/common_utils'; -import { escapeFileUrl } from '../lib/utils/url_utility'; -import { __ } from '../locale'; import App from './components/app.vue'; import Breadcrumbs from './components/breadcrumbs.vue'; import DirectoryDownloadLinks from './components/directory_download_links.vue'; import LastCommit from './components/last_commit.vue'; import apolloProvider from './graphql'; +import commitsQuery from './queries/commits.query.graphql'; +import projectPathQuery from './queries/project_path.query.graphql'; +import projectShortPathQuery from './queries/project_short_path.query.graphql'; +import refsQuery from './queries/ref.query.graphql'; import createRouter from './router'; import { updateFormAction } from './utils/dom'; import { setTitle } from './utils/title'; @@ -19,13 +23,32 @@ export default function setupVueRepositoryList() { const { projectPath, projectShortPath, ref, escapedRef, fullName } = dataset; const router = createRouter(projectPath, escapedRef); - apolloProvider.clients.defaultClient.cache.writeData({ + apolloProvider.clients.defaultClient.cache.writeQuery({ + query: commitsQuery, + data: { + commits: [], + }, + }); + + apolloProvider.clients.defaultClient.cache.writeQuery({ + query: projectPathQuery, data: { projectPath, + }, + }); + + apolloProvider.clients.defaultClient.cache.writeQuery({ + query: projectShortPathQuery, + data: { projectShortPath, + }, + }); + + apolloProvider.clients.defaultClient.cache.writeQuery({ + query: refsQuery, + data: { ref, escapedRef, - commits: [], }, }); @@ -55,6 +78,8 @@ export default function setupVueRepositoryList() { const { canCollaborate, canEditTree, + canPushCode, + selectedBranch, newBranchPath, newTagPath, newBlobPath, @@ -65,8 +90,7 @@ export default function setupVueRepositoryList() { newDirPath, } = breadcrumbEl.dataset; - router.afterEach(({ params: { path = '/' } }) => { - updateFormAction('.js-upload-blob-form', uploadPath, path); + router.afterEach(({ params: { path } }) => { updateFormAction('.js-create-dir-form', newDirPath, path); }); @@ -81,12 +105,16 @@ export default function setupVueRepositoryList() { currentPath: this.$route.params.path, canCollaborate: parseBoolean(canCollaborate), canEditTree: parseBoolean(canEditTree), + canPushCode: parseBoolean(canPushCode), + originalBranch: ref, + selectedBranch, newBranchPath, newTagPath, newBlobPath, forkNewBlobPath, forkNewDirectoryPath, forkUploadBlobPath, + uploadPath, }, }); }, diff --git a/app/assets/javascripts/repository/pages/blob.vue b/app/assets/javascripts/repository/pages/blob.vue new file mode 100644 index 00000000000..27af398be09 --- /dev/null +++ b/app/assets/javascripts/repository/pages/blob.vue @@ -0,0 +1,22 @@ +<script> +// This file is in progress and behind a feature flag, please see the following issue for more: +// https://gitlab.com/gitlab-org/gitlab/-/issues/323200 + +import BlobContentViewer from '../components/blob_content_viewer.vue'; + +export default { + components: { + BlobContentViewer, + }, + props: { + path: { + type: String, + required: true, + }, + }, +}; +</script> + +<template> + <blob-content-viewer :path="path" /> +</template> diff --git a/app/assets/javascripts/repository/queries/blob_info.query.graphql b/app/assets/javascripts/repository/queries/blob_info.query.graphql new file mode 100644 index 00000000000..e0bbf12f3eb --- /dev/null +++ b/app/assets/javascripts/repository/queries/blob_info.query.graphql @@ -0,0 +1,30 @@ +query getBlobInfo($projectPath: ID!, $filePath: String!) { + project(fullPath: $projectPath) { + id + repository { + blobs(path: $filePath) { + name + size + rawBlob + type + fileType + tooLarge + path + editBlobPath + ideEditPath + storedExternally + rawPath + externalStorageUrl + replacePath + deletePath + canLock + isLocked + lockLink + canModifyBlob + forkPath + simpleViewer + richViewer + } + } + } +} diff --git a/app/assets/javascripts/repository/router.js b/app/assets/javascripts/repository/router.js index ad6e32d7055..c7f7451fb55 100644 --- a/app/assets/javascripts/repository/router.js +++ b/app/assets/javascripts/repository/router.js @@ -2,6 +2,7 @@ import { escapeRegExp } from 'lodash'; import Vue from 'vue'; import VueRouter from 'vue-router'; import { joinPaths } from '../lib/utils/url_utility'; +import BlobPage from './pages/blob.vue'; import IndexPage from './pages/index.vue'; import TreePage from './pages/tree.vue'; @@ -15,6 +16,13 @@ export default function createRouter(base, baseRef) { }), }; + const blobPathRoute = { + component: BlobPage, + props: (route) => ({ + path: route.params.path, + }), + }; + return new VueRouter({ mode: 'history', base: joinPaths(gon.relative_url_root || '', base), @@ -32,6 +40,18 @@ export default function createRouter(base, baseRef) { ...treePathRoute, }, { + name: 'blobPathDecoded', + // Sometimes the ref needs decoding depending on how the backend sends it to us + path: `(/-)?/blob/${decodeURI(baseRef)}/:path*`, + ...blobPathRoute, + }, + { + name: 'blobPath', + // Support without decoding as well just in case the ref doesn't need to be decoded + path: `(/-)?/blob/${escapeRegExp(baseRef)}/:path*`, + ...blobPathRoute, + }, + { path: '/', name: 'projectRoot', component: IndexPage, |