summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/repository
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/repository')
-rw-r--r--app/assets/javascripts/repository/components/blob_content_viewer.vue95
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue20
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue14
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/index.js61
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/lfs_viewer.vue38
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue14
-rw-r--r--app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue9
-rw-r--r--app/assets/javascripts/repository/components/last_commit.vue4
-rw-r--r--app/assets/javascripts/repository/components/table/parent_row.vue13
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue1
-rw-r--r--app/assets/javascripts/repository/constants.js51
-rw-r--r--app/assets/javascripts/repository/fragmentTypes.json1
-rw-r--r--app/assets/javascripts/repository/graphql.js9
-rw-r--r--app/assets/javascripts/repository/queries/blob_info.query.graphql13
14 files changed, 191 insertions, 152 deletions
diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue
index 9368d7e6058..52963b49f68 100644
--- a/app/assets/javascripts/repository/components/blob_content_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue
@@ -9,12 +9,14 @@ import axios from '~/lib/utils/axios_utils';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import getRefMixin from '../mixins/get_ref';
import blobInfoQuery from '../queries/blob_info.query.graphql';
+import { DEFAULT_BLOB_INFO, TEXT_FILE_TYPE, LFS_STORAGE } from '../constants';
import BlobButtonGroup from './blob_button_group.vue';
import BlobEdit from './blob_edit.vue';
import ForkSuggestion from './fork_suggestion.vue';
-import { loadViewer, viewerProps } from './blob_viewers';
+import { loadViewer } from './blob_viewers';
export default {
i18n: {
@@ -29,7 +31,7 @@ export default {
GlButton,
ForkSuggestion,
},
- mixins: [getRefMixin],
+ mixins: [getRefMixin, glFeatureFlagMixin()],
inject: {
originalBranch: {
default: '',
@@ -43,12 +45,11 @@ export default {
projectPath: this.projectPath,
filePath: this.path,
ref: this.originalBranch || this.ref,
+ shouldFetchRawText: Boolean(this.glFeatures.highlightJs),
};
},
result() {
- this.switchViewer(
- this.hasRichViewer && !window.location.hash ? RICH_BLOB_VIEWER : SIMPLE_BLOB_VIEWER,
- );
+ this.switchViewer(this.hasRichViewer ? RICH_BLOB_VIEWER : SIMPLE_BLOB_VIEWER);
},
error() {
this.displayError();
@@ -78,50 +79,7 @@ export default {
isBinary: false,
isLoadingLegacyViewer: false,
activeViewerType: SIMPLE_BLOB_VIEWER,
- project: {
- userPermissions: {
- pushCode: false,
- downloadCode: false,
- createMergeRequestIn: false,
- forkProject: false,
- },
- pathLocks: {
- nodes: [],
- },
- repository: {
- empty: true,
- blobs: {
- nodes: [
- {
- name: '',
- size: '',
- rawTextBlob: '',
- type: '',
- fileType: '',
- tooLarge: false,
- path: '',
- editBlobPath: '',
- ideEditPath: '',
- forkAndEditPath: '',
- ideForkAndEditPath: '',
- storedExternally: false,
- externalStorage: '',
- canModifyBlob: false,
- canCurrentUserPushToBranch: false,
- archived: false,
- rawPath: '',
- externalStorageUrl: '',
- replacePath: '',
- pipelineEditorPath: '',
- deletePath: '',
- simpleViewer: {},
- richViewer: null,
- webPath: '',
- },
- ],
- },
- },
- },
+ project: DEFAULT_BLOB_INFO,
};
},
computed: {
@@ -132,7 +90,7 @@ export default {
return this.$apollo.queries.project.loading;
},
isBinaryFileType() {
- return this.isBinary || this.blobInfo.simpleViewer?.fileType !== 'text';
+ return this.isBinary || this.blobInfo.simpleViewer?.fileType !== TEXT_FILE_TYPE;
},
blobInfo() {
const nodes = this.project?.repository?.blobs?.nodes || [];
@@ -151,11 +109,16 @@ export default {
},
blobViewer() {
const { fileType } = this.viewer;
- return loadViewer(fileType);
+ return this.shouldLoadLegacyViewer ? null : loadViewer(fileType, this.isUsingLfs);
},
- viewerProps() {
- const { fileType } = this.viewer;
- return viewerProps(fileType, this.blobInfo);
+ shouldLoadLegacyViewer() {
+ return this.viewer.fileType === TEXT_FILE_TYPE && !this.glFeatures.highlightJs;
+ },
+ legacyViewerLoaded() {
+ return (
+ (this.activeViewerType === SIMPLE_BLOB_VIEWER && this.legacySimpleViewer) ||
+ (this.activeViewerType === RICH_BLOB_VIEWER && this.legacyRichViewer)
+ );
},
canLock() {
const { pushCode, downloadCode } = this.project.userPermissions;
@@ -183,18 +146,23 @@ export default {
? this.blobInfo.ideForkAndEditPath
: this.blobInfo.forkAndEditPath;
},
+ isUsingLfs() {
+ return this.blobInfo.storedExternally && this.blobInfo.externalStorage === LFS_STORAGE;
+ },
},
methods: {
- loadLegacyViewer(type) {
- if (this.legacyViewerLoaded(type)) {
+ loadLegacyViewer() {
+ if (this.legacyViewerLoaded) {
return;
}
+ const type = this.activeViewerType;
+
this.isLoadingLegacyViewer = true;
axios
.get(`${this.blobInfo.webPath}?format=json&viewer=${type}`)
.then(({ data: { html, binary } }) => {
- if (type === 'simple') {
+ if (type === SIMPLE_BLOB_VIEWER) {
this.legacySimpleViewer = html;
} else {
this.legacyRichViewer = html;
@@ -205,12 +173,6 @@ export default {
})
.catch(() => this.displayError());
},
- legacyViewerLoaded(type) {
- return (
- (type === SIMPLE_BLOB_VIEWER && this.legacySimpleViewer) ||
- (type === RICH_BLOB_VIEWER && this.legacyRichViewer)
- );
- },
displayError() {
createFlash({ message: __('An error occurred while loading the file. Please try again.') });
},
@@ -218,7 +180,7 @@ export default {
this.activeViewerType = newViewer || SIMPLE_BLOB_VIEWER;
if (!this.blobViewer) {
- this.loadLegacyViewer(this.activeViewerType);
+ this.loadLegacyViewer();
}
},
editBlob(target) {
@@ -243,10 +205,11 @@ export default {
<div v-if="blobInfo && !isLoading" class="file-holder">
<blob-header
:blob="blobInfo"
- :hide-viewer-switcher="!hasRichViewer || isBinaryFileType"
+ :hide-viewer-switcher="!hasRichViewer || isBinaryFileType || isUsingLfs"
:is-binary="isBinaryFileType"
:active-viewer-type="viewer.type"
:has-render-error="hasRenderError"
+ :show-path="false"
@viewer-changed="switchViewer"
>
<template #actions>
@@ -303,7 +266,7 @@ export default {
:hide-line-numbers="true"
:loading="isLoadingLegacyViewer"
/>
- <component :is="blobViewer" v-else v-bind="viewerProps" class="blob-viewer" />
+ <component :is="blobViewer" v-else :blob="blobInfo" class="blob-viewer" />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue
index 48fa33eb558..f7b318c64d9 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_viewers/download_viewer.vue
@@ -9,19 +9,17 @@ export default {
GlLink,
},
props: {
- fileName: {
- type: String,
+ blob: {
+ type: Object,
required: true,
},
- filePath: {
- type: String,
- required: true,
- },
- fileSize: {
- type: Number,
- required: false,
- default: 0,
- },
+ },
+ data() {
+ return {
+ fileName: this.blob.name,
+ filePath: this.blob.rawPath,
+ fileSize: this.blob.rawSize || 0,
+ };
},
computed: {
downloadFileSize() {
diff --git a/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue
index 83d36209bb3..5027f7877aa 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue
@@ -1,15 +1,17 @@
<script>
export default {
props: {
- url: {
- type: String,
- required: true,
- },
- alt: {
- type: String,
+ blob: {
+ type: Object,
required: true,
},
},
+ data() {
+ return {
+ url: this.blob.rawPath,
+ alt: this.blob.name,
+ };
+ },
};
</script>
<template>
diff --git a/app/assets/javascripts/repository/components/blob_viewers/index.js b/app/assets/javascripts/repository/components/blob_viewers/index.js
index 8f6f2d15215..e942f59e7d8 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/index.js
+++ b/app/assets/javascripts/repository/components/blob_viewers/index.js
@@ -1,48 +1,19 @@
-export const loadViewer = (type) => {
- switch (type) {
- case 'empty':
- return () => import(/* webpackChunkName: 'blob_empty_viewer' */ './empty_viewer.vue');
- case 'text':
- return gon.features.highlightJs
- ? () =>
- import(
- /* webpackChunkName: 'blob_text_viewer' */ '~/vue_shared/components/source_viewer.vue'
- )
- : null;
- case 'download':
- return () => import(/* webpackChunkName: 'blob_download_viewer' */ './download_viewer.vue');
- case 'image':
- return () => import(/* webpackChunkName: 'blob_image_viewer' */ './image_viewer.vue');
- case 'video':
- return () => import(/* webpackChunkName: 'blob_video_viewer' */ './video_viewer.vue');
- case 'pdf':
- return () => import(/* webpackChunkName: 'blob_pdf_viewer' */ './pdf_viewer.vue');
- default:
- return null;
- }
+const viewers = {
+ download: () => import('./download_viewer.vue'),
+ image: () => import('./image_viewer.vue'),
+ video: () => import('./video_viewer.vue'),
+ empty: () => import('./empty_viewer.vue'),
+ text: () => import('~/vue_shared/components/source_viewer/source_viewer.vue'),
+ pdf: () => import('./pdf_viewer.vue'),
+ lfs: () => import('./lfs_viewer.vue'),
};
-export const viewerProps = (type, blob) => {
- return {
- text: {
- content: blob.rawTextBlob,
- autoDetect: true, // We'll eventually disable autoDetect and pass the language explicitly to reduce the footprint (https://gitlab.com/gitlab-org/gitlab/-/issues/348145)
- },
- download: {
- fileName: blob.name,
- filePath: blob.rawPath,
- fileSize: blob.rawSize,
- },
- image: {
- url: blob.rawPath,
- alt: blob.name,
- },
- video: {
- url: blob.rawPath,
- },
- pdf: {
- url: blob.rawPath,
- fileSize: blob.rawSize,
- },
- }[type];
+export const loadViewer = (type, isUsingLfs) => {
+ let viewer = viewers[type];
+
+ if (!viewer && isUsingLfs) {
+ viewer = viewers.lfs;
+ }
+
+ return viewer;
};
diff --git a/app/assets/javascripts/repository/components/blob_viewers/lfs_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/lfs_viewer.vue
new file mode 100644
index 00000000000..6dc7e10662e
--- /dev/null
+++ b/app/assets/javascripts/repository/components/blob_viewers/lfs_viewer.vue
@@ -0,0 +1,38 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+export default {
+ i18n: {
+ lfsText: __(
+ 'This content could not be displayed because it is stored in LFS. You can %{linkStart}download it%{linkEnd} instead.',
+ ),
+ },
+ components: {
+ GlLink,
+ GlSprintf,
+ },
+ props: {
+ blob: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ fileName: this.blob.name,
+ filePath: this.blob.rawPath,
+ };
+ },
+};
+</script>
+
+<template>
+ <div class="gl-text-center gl-py-13 gl-bg-gray-50" data-type="lfs">
+ <gl-sprintf :message="$options.i18n.lfsText">
+ <template #link="{ content }">
+ <gl-link :href="filePath" :download="fileName" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+</template>
diff --git a/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue
index 803a357df52..c3df5984426 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_viewers/pdf_viewer.vue
@@ -11,17 +11,17 @@ export default {
tooLargeButtonText: __('Download PDF'),
},
props: {
- url: {
- type: String,
- required: true,
- },
- fileSize: {
- type: Number,
+ blob: {
+ type: Object,
required: true,
},
},
data() {
- return { totalPages: 0 };
+ return {
+ url: this.blob.rawPath,
+ fileSize: this.blob.rawSize,
+ totalPages: 0,
+ };
},
computed: {
tooLargeToDisplay() {
diff --git a/app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue
index dec0c4802ca..260b831f4d1 100644
--- a/app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_viewers/video_viewer.vue
@@ -1,11 +1,16 @@
<script>
export default {
props: {
- url: {
- type: String,
+ blob: {
+ type: Object,
required: true,
},
},
+ data() {
+ return {
+ url: this.blob.rawPath,
+ };
+ },
};
</script>
<template>
diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue
index 43e114a91d3..c3d121505b6 100644
--- a/app/assets/javascripts/repository/components/last_commit.vue
+++ b/app/assets/javascripts/repository/components/last_commit.vue
@@ -139,8 +139,10 @@ export default {
/>
<gl-button
v-if="commit.descriptionHtml"
+ v-gl-tooltip
:class="{ open: showDescription }"
- :aria-label="__('Show commit description')"
+ :title="__('Toggle commit description')"
+ :aria-label="__('Toggle commit description')"
class="text-expander gl-vertical-align-bottom!"
icon="ellipsis_h"
@click="toggleShowDescription"
diff --git a/app/assets/javascripts/repository/components/table/parent_row.vue b/app/assets/javascripts/repository/components/table/parent_row.vue
index fb0e505a16e..8a081944600 100644
--- a/app/assets/javascripts/repository/components/table/parent_row.vue
+++ b/app/assets/javascripts/repository/components/table/parent_row.vue
@@ -1,10 +1,13 @@
<script>
-import { GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
export default {
components: {
GlLoadingIcon,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
commitRef: {
type: String,
@@ -41,7 +44,13 @@ export default {
<template>
<tr class="tree-item">
- <td colspan="3" class="tree-item-file-name" @click.self="clickRow">
+ <td
+ v-gl-tooltip.left.viewport
+ :title="__('Go to parent directory')"
+ colspan="3"
+ class="tree-item-file-name"
+ @click.self="clickRow"
+ >
<gl-loading-icon
v-if="parentPath === loadingPath"
size="sm"
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index 8fcec5fb893..7aac35e7613 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -195,6 +195,7 @@ export default {
projectPath: this.projectPath,
filePath: this.path,
ref: this.ref,
+ shouldFetchRawText: Boolean(this.glFeatures.highlightJs),
});
},
apolloQuery(query, variables) {
diff --git a/app/assets/javascripts/repository/constants.js b/app/assets/javascripts/repository/constants.js
index d01757d6141..e206d9bfbd2 100644
--- a/app/assets/javascripts/repository/constants.js
+++ b/app/assets/javascripts/repository/constants.js
@@ -25,3 +25,54 @@ export const PDF_MAX_FILE_SIZE = 10000000; // 10 MB
export const PDF_MAX_PAGE_LIMIT = 50;
export const ROW_APPEAR_DELAY = 150;
+
+export const DEFAULT_BLOB_INFO = {
+ userPermissions: {
+ pushCode: false,
+ downloadCode: false,
+ createMergeRequestIn: false,
+ forkProject: false,
+ },
+ pathLocks: {
+ nodes: [],
+ },
+ repository: {
+ empty: true,
+ blobs: {
+ nodes: [
+ {
+ name: '',
+ size: '',
+ rawTextBlob: '',
+ type: '',
+ fileType: '',
+ tooLarge: false,
+ path: '',
+ editBlobPath: '',
+ ideEditPath: '',
+ forkAndEditPath: '',
+ ideForkAndEditPath: '',
+ storedExternally: false,
+ externalStorage: '',
+ environmentFormattedExternalUrl: '',
+ environmentExternalUrlForRouteMap: '',
+ canModifyBlob: false,
+ canCurrentUserPushToBranch: false,
+ archived: false,
+ rawPath: '',
+ externalStorageUrl: '',
+ replacePath: '',
+ pipelineEditorPath: '',
+ deletePath: '',
+ simpleViewer: {},
+ richViewer: null,
+ webPath: '',
+ },
+ ],
+ },
+ },
+};
+
+export const TEXT_FILE_TYPE = 'text';
+
+export const LFS_STORAGE = 'lfs';
diff --git a/app/assets/javascripts/repository/fragmentTypes.json b/app/assets/javascripts/repository/fragmentTypes.json
deleted file mode 100644
index 949ebca432b..00000000000
--- a/app/assets/javascripts/repository/fragmentTypes.json
+++ /dev/null
@@ -1 +0,0 @@
-{"__schema":{"types":[{"kind":"INTERFACE","name":"Entry","possibleTypes":[{"name":"Blob"},{"name":"Submodule"},{"name":"TreeEntry"}]}]}}
diff --git a/app/assets/javascripts/repository/graphql.js b/app/assets/javascripts/repository/graphql.js
index 96d712ce9b4..29aabe1b00f 100644
--- a/app/assets/javascripts/repository/graphql.js
+++ b/app/assets/javascripts/repository/graphql.js
@@ -1,19 +1,11 @@
-import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import axios from '~/lib/utils/axios_utils';
-import introspectionQueryResultData from './fragmentTypes.json';
import { fetchLogsTree } from './log_tree';
Vue.use(VueApollo);
-// We create a fragment matcher so that we can create a fragment from an interface
-// Without this, Apollo throws a heuristic fragment matcher warning
-const fragmentMatcher = new IntrospectionFragmentMatcher({
- introspectionQueryResultData,
-});
-
const defaultClient = createDefaultClient(
{
Query: {
@@ -43,7 +35,6 @@ const defaultClient = createDefaultClient(
},
{
cacheConfig: {
- fragmentMatcher,
dataIdFromObject: (obj) => {
/* eslint-disable @gitlab/require-i18n-strings */
// eslint-disable-next-line no-underscore-dangle
diff --git a/app/assets/javascripts/repository/queries/blob_info.query.graphql b/app/assets/javascripts/repository/queries/blob_info.query.graphql
index ae20a0f0bc4..78323fdc5f4 100644
--- a/app/assets/javascripts/repository/queries/blob_info.query.graphql
+++ b/app/assets/javascripts/repository/queries/blob_info.query.graphql
@@ -1,6 +1,11 @@
#import "ee_else_ce/repository/queries/path_locks.fragment.graphql"
-query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) {
+query getBlobInfo(
+ $projectPath: ID!
+ $filePath: String!
+ $ref: String!
+ $shouldFetchRawText: Boolean!
+) {
project(fullPath: $projectPath) {
userPermissions {
pushCode
@@ -18,18 +23,22 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) {
name
size
rawSize
- rawTextBlob
+ rawTextBlob @include(if: $shouldFetchRawText)
fileType
+ language
path
editBlobPath
ideEditPath
forkAndEditPath
ideForkAndEditPath
+ environmentFormattedExternalUrl
+ environmentExternalUrlForRouteMap
canModifyBlob
canCurrentUserPushToBranch
archived
storedExternally
externalStorage
+ externalStorageUrl
rawPath
replacePath
pipelineEditorPath