summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/api.js5
-rw-r--r--app/assets/javascripts/frequent_items/store/mutations.js3
-rw-r--r--app/assets/javascripts/jobs/components/log/log.vue26
-rw-r--r--app/assets/javascripts/notes/components/diff_discussion_header.vue133
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue111
-rw-r--r--app/assets/javascripts/releases/detail/components/app.vue46
-rw-r--r--app/assets/javascripts/releases/detail/store/state.js1
-rw-r--r--app/assets/javascripts/repository/index.js14
-rw-r--r--app/assets/javascripts/repository/pages/index.vue18
-rw-r--r--app/assets/javascripts/repository/pages/tree.vue18
-rw-r--r--app/assets/javascripts/repository/utils/dom.js4
-rw-r--r--app/assets/javascripts/repository/utils/title.js8
-rw-r--r--app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue42
-rw-r--r--app/helpers/application_settings_helper.rb4
-rw-r--r--app/helpers/releases_helper.rb3
-rw-r--r--app/helpers/repository_languages_helper.rb2
-rw-r--r--app/models/application_setting.rb11
-rw-r--r--app/models/application_setting_implementation.rb2
-rw-r--r--app/views/admin/application_settings/integrations.html.haml2
-rw-r--r--app/views/layouts/_head.html.haml1
-rw-r--r--app/views/projects/_files.html.haml2
-rw-r--r--app/views/projects/_home_panel.html.haml2
-rw-r--r--app/views/projects/tree/_readme.html.haml2
23 files changed, 284 insertions, 176 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 735cbb8e356..aee9990bc0b 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -113,10 +113,9 @@ const Api = {
.get(url, {
params: Object.assign(defaults, options),
})
- .then(({ data }) => {
+ .then(({ data, headers }) => {
callback(data);
-
- return data;
+ return { data, headers };
});
},
diff --git a/app/assets/javascripts/frequent_items/store/mutations.js b/app/assets/javascripts/frequent_items/store/mutations.js
index 41b660a243f..92ac3a2c94d 100644
--- a/app/assets/javascripts/frequent_items/store/mutations.js
+++ b/app/assets/javascripts/frequent_items/store/mutations.js
@@ -47,7 +47,8 @@ export default {
hasSearchQuery: true,
});
},
- [types.RECEIVE_SEARCHED_ITEMS_SUCCESS](state, rawItems) {
+ [types.RECEIVE_SEARCHED_ITEMS_SUCCESS](state, results) {
+ const rawItems = results.data;
Object.assign(state, {
items: rawItems.map(rawItem => ({
id: rawItem.id,
diff --git a/app/assets/javascripts/jobs/components/log/log.vue b/app/assets/javascripts/jobs/components/log/log.vue
index ef126166e8b..03a697d11ed 100644
--- a/app/assets/javascripts/jobs/components/log/log.vue
+++ b/app/assets/javascripts/jobs/components/log/log.vue
@@ -11,11 +11,35 @@ export default {
computed: {
...mapState(['traceEndpoint', 'trace', 'isTraceComplete']),
},
+ updated() {
+ this.$nextTick(() => {
+ this.handleScrollDown();
+ });
+ },
+ mounted() {
+ this.$nextTick(() => {
+ this.handleScrollDown();
+ });
+ },
methods: {
- ...mapActions(['toggleCollapsibleLine']),
+ ...mapActions(['toggleCollapsibleLine', 'scrollBottom']),
handleOnClickCollapsibleLine(section) {
this.toggleCollapsibleLine(section);
},
+ /**
+ * The job log is sent in HTML, which means we need to use `v-html` to render it
+ * Using the updated hook with $nextTick is not enough to wait for the DOM to be updated
+ * in this case because it runs before `v-html` has finished running, since there's no
+ * Vue binding.
+ * In order to scroll the page down after `v-html` has finished, we need to use setTimeout
+ */
+ handleScrollDown() {
+ if (this.isScrolledToBottomBeforeReceivingTrace) {
+ setTimeout(() => {
+ this.scrollBottom();
+ }, 0);
+ }
+ },
},
};
</script>
diff --git a/app/assets/javascripts/notes/components/diff_discussion_header.vue b/app/assets/javascripts/notes/components/diff_discussion_header.vue
new file mode 100644
index 00000000000..4c9075912ee
--- /dev/null
+++ b/app/assets/javascripts/notes/components/diff_discussion_header.vue
@@ -0,0 +1,133 @@
+<script>
+import { mapActions } from 'vuex';
+import _ from 'underscore';
+
+import { s__, __, sprintf } from '~/locale';
+import { truncateSha } from '~/lib/utils/text_utility';
+
+import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
+import noteEditedText from './note_edited_text.vue';
+import noteHeader from './note_header.vue';
+
+export default {
+ name: 'DiffDiscussionHeader',
+ components: {
+ userAvatarLink,
+ noteEditedText,
+ noteHeader,
+ },
+ props: {
+ discussion: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ notes() {
+ return this.discussion.notes;
+ },
+ firstNote() {
+ return this.notes[0];
+ },
+ lastNote() {
+ return this.notes[this.notes.length - 1];
+ },
+ author() {
+ return this.firstNote.author;
+ },
+ resolvedText() {
+ return this.discussion.resolved_by_push ? __('Automatically resolved') : __('Resolved');
+ },
+ lastUpdatedBy() {
+ return this.notes.length > 1 ? this.lastNote.author : null;
+ },
+ lastUpdatedAt() {
+ return this.notes.length > 1 ? this.lastNote.created_at : null;
+ },
+ headerText() {
+ const linkStart = `<a href="${_.escape(this.discussion.discussion_path)}">`;
+ const linkEnd = '</a>';
+
+ const { commit_id: commitId } = this.discussion;
+ let commitDisplay = commitId;
+
+ if (commitId) {
+ commitDisplay = `<span class="commit-sha">${truncateSha(commitId)}</span>`;
+ }
+
+ const {
+ for_commit: isForCommit,
+ diff_discussion: isDiffDiscussion,
+ active: isActive,
+ } = this.discussion;
+
+ let text = s__('MergeRequests|started a thread');
+ if (isForCommit) {
+ text = s__(
+ 'MergeRequests|started a thread on commit %{linkStart}%{commitDisplay}%{linkEnd}',
+ );
+ } else if (isDiffDiscussion && commitId) {
+ text = isActive
+ ? s__('MergeRequests|started a thread on commit %{linkStart}%{commitDisplay}%{linkEnd}')
+ : s__(
+ 'MergeRequests|started a thread on an outdated change in commit %{linkStart}%{commitDisplay}%{linkEnd}',
+ );
+ } else if (isDiffDiscussion) {
+ text = isActive
+ ? s__('MergeRequests|started a thread on %{linkStart}the diff%{linkEnd}')
+ : s__(
+ 'MergeRequests|started a thread on %{linkStart}an old version of the diff%{linkEnd}',
+ );
+ }
+
+ return sprintf(text, { commitDisplay, linkStart, linkEnd }, false);
+ },
+ },
+ methods: {
+ ...mapActions(['toggleDiscussion']),
+ toggleDiscussionHandler() {
+ this.toggleDiscussion({ discussionId: this.discussion.id });
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="discussion-header note-wrapper">
+ <div v-once class="timeline-icon align-self-start flex-shrink-0">
+ <user-avatar-link
+ v-if="author"
+ :link-href="author.path"
+ :img-src="author.avatar_url"
+ :img-alt="author.name"
+ :img-size="40"
+ />
+ </div>
+ <div class="timeline-content w-100">
+ <note-header
+ :author="author"
+ :created-at="firstNote.created_at"
+ :note-id="firstNote.id"
+ :include-toggle="true"
+ :expanded="discussion.expanded"
+ @toggleHandler="toggleDiscussionHandler"
+ >
+ <span v-html="headerText"></span>
+ </note-header>
+ <note-edited-text
+ v-if="discussion.resolved"
+ :edited-at="discussion.resolved_at"
+ :edited-by="discussion.resolved_by"
+ :action-text="resolvedText"
+ class-name="discussion-headline-light js-discussion-headline"
+ />
+ <note-edited-text
+ v-else-if="lastUpdatedAt"
+ :edited-at="lastUpdatedAt"
+ :edited-by="lastUpdatedBy"
+ :action-text="__('Last updated')"
+ class-name="discussion-headline-light js-discussion-headline"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index cb1975a8962..47ec740b63a 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -1,18 +1,15 @@
<script>
-import _ from 'underscore';
import { mapActions, mapGetters } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui';
-import { truncateSha } from '~/lib/utils/text_utility';
-import { s__, __, sprintf } from '~/locale';
+import { s__, __ } from '~/locale';
import { clearDraft, getDiscussionReplyKey } from '~/lib/utils/autosave';
import icon from '~/vue_shared/components/icon.vue';
import diffLineNoteFormMixin from 'ee_else_ce/notes/mixins/diff_line_note_form';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import Flash from '../../flash';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
-import noteHeader from './note_header.vue';
+import diffDiscussionHeader from './diff_discussion_header.vue';
import noteSignedOutWidget from './note_signed_out_widget.vue';
-import noteEditedText from './note_edited_text.vue';
import noteForm from './note_form.vue';
import diffWithNote from './diff_with_note.vue';
import noteable from '../mixins/noteable';
@@ -27,9 +24,8 @@ export default {
components: {
icon,
userAvatarLink,
- noteHeader,
+ diffDiscussionHeader,
noteSignedOutWidget,
- noteEditedText,
noteForm,
DraftNote: () => import('ee_component/batch_comments/components/draft_note.vue'),
TimelineEntryItem,
@@ -92,9 +88,6 @@ export default {
currentUser() {
return this.getUserData;
},
- author() {
- return this.firstNote.author;
- },
autosaveKey() {
return getDiscussionReplyKey(this.firstNote.noteable_type, this.discussion.id);
},
@@ -104,27 +97,6 @@ export default {
firstNote() {
return this.discussion.notes.slice(0, 1)[0];
},
- lastUpdatedBy() {
- const { notes } = this.discussion;
-
- if (notes.length > 1) {
- return notes[notes.length - 1].author;
- }
-
- return null;
- },
- lastUpdatedAt() {
- const { notes } = this.discussion;
-
- if (notes.length > 1) {
- return notes[notes.length - 1].created_at;
- }
-
- return null;
- },
- resolvedText() {
- return this.discussion.resolved_by_push ? __('Automatically resolved') : __('Resolved');
- },
shouldShowJumpToNextDiscussion() {
return this.showJumpToNextDiscussion(this.discussionsByDiffOrder ? 'diff' : 'discussion');
},
@@ -150,40 +122,6 @@ export default {
shouldHideDiscussionBody() {
return this.shouldRenderDiffs && !this.isExpanded;
},
- actionText() {
- const linkStart = `<a href="${_.escape(this.discussion.discussion_path)}">`;
- const linkEnd = '</a>';
-
- let { commit_id: commitId } = this.discussion;
- if (commitId) {
- commitId = `<span class="commit-sha">${truncateSha(commitId)}</span>`;
- }
-
- const {
- for_commit: isForCommit,
- diff_discussion: isDiffDiscussion,
- active: isActive,
- } = this.discussion;
-
- let text = s__('MergeRequests|started a thread');
- if (isForCommit) {
- text = s__('MergeRequests|started a thread on commit %{linkStart}%{commitId}%{linkEnd}');
- } else if (isDiffDiscussion && commitId) {
- text = isActive
- ? s__('MergeRequests|started a thread on commit %{linkStart}%{commitId}%{linkEnd}')
- : s__(
- 'MergeRequests|started a thread on an outdated change in commit %{linkStart}%{commitId}%{linkEnd}',
- );
- } else if (isDiffDiscussion) {
- text = isActive
- ? s__('MergeRequests|started a thread on %{linkStart}the diff%{linkEnd}')
- : s__(
- 'MergeRequests|started a thread on %{linkStart}an old version of the diff%{linkEnd}',
- );
- }
-
- return sprintf(text, { commitId, linkStart, linkEnd }, false);
- },
diffLine() {
if (this.line) {
return this.line;
@@ -208,16 +146,11 @@ export default {
methods: {
...mapActions([
'saveNote',
- 'toggleDiscussion',
'removePlaceholderNotes',
'toggleResolveNote',
'expandDiscussion',
'removeConvertedDiscussion',
]),
- truncateSha,
- toggleDiscussionHandler() {
- this.toggleDiscussion({ discussionId: this.discussion.id });
- },
showReplyForm() {
this.isReplying = true;
},
@@ -311,43 +244,7 @@ export default {
class="discussion js-discussion-container"
data-qa-selector="discussion_content"
>
- <div v-if="shouldRenderDiffs" class="discussion-header note-wrapper">
- <div v-once class="timeline-icon align-self-start flex-shrink-0">
- <user-avatar-link
- v-if="author"
- :link-href="author.path"
- :img-src="author.avatar_url"
- :img-alt="author.name"
- :img-size="40"
- />
- </div>
- <div class="timeline-content w-100">
- <note-header
- :author="author"
- :created-at="firstNote.created_at"
- :note-id="firstNote.id"
- :include-toggle="true"
- :expanded="discussion.expanded"
- @toggleHandler="toggleDiscussionHandler"
- >
- <span v-html="actionText"></span>
- </note-header>
- <note-edited-text
- v-if="discussion.resolved"
- :edited-at="discussion.resolved_at"
- :edited-by="discussion.resolved_by"
- :action-text="resolvedText"
- class-name="discussion-headline-light js-discussion-headline"
- />
- <note-edited-text
- v-else-if="lastUpdatedAt"
- :edited-at="lastUpdatedAt"
- :edited-by="lastUpdatedBy"
- action-text="Last updated"
- class-name="discussion-headline-light js-discussion-headline"
- />
- </div>
- </div>
+ <diff-discussion-header v-if="shouldRenderDiffs" :discussion="discussion" />
<div v-if="!shouldHideDiscussionBody" class="discussion-body">
<component
:is="wrapperComponent"
diff --git a/app/assets/javascripts/releases/detail/components/app.vue b/app/assets/javascripts/releases/detail/components/app.vue
index 54a441de886..073cfcd7694 100644
--- a/app/assets/javascripts/releases/detail/components/app.vue
+++ b/app/assets/javascripts/releases/detail/components/app.vue
@@ -1,6 +1,7 @@
<script>
import { mapState, mapActions } from 'vuex';
import { GlButton, GlFormInput, GlFormGroup } from '@gitlab/ui';
+import _ from 'underscore';
import { __, sprintf } from '~/locale';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
@@ -23,6 +24,7 @@ export default {
'markdownDocsPath',
'markdownPreviewPath',
'releasesPagePath',
+ 'updateReleaseApiDocsPath',
]),
showForm() {
return !this.isFetchingRelease && !this.fetchError;
@@ -42,6 +44,20 @@ export default {
tagName() {
return this.$store.state.release.tagName;
},
+ tagNameHintText() {
+ return sprintf(
+ __(
+ 'Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}',
+ ),
+ {
+ linkStart: `<a href="${_.escape(
+ this.updateReleaseApiDocsPath,
+ )}" target="_blank" rel="noopener noreferrer">`,
+ linkEnd: '</a>',
+ },
+ false,
+ );
+ },
releaseTitle: {
get() {
return this.$store.state.release.name;
@@ -77,22 +93,22 @@ export default {
<div class="d-flex flex-column">
<p class="pt-3 js-subtitle-text" v-html="subtitleText"></p>
<form v-if="showForm" @submit.prevent="updateRelease()">
- <div class="row">
- <gl-form-group class="col-md-6 col-lg-5 col-xl-4">
- <label for="git-ref">{{ __('Tag name') }}</label>
- <gl-form-input
- id="git-ref"
- v-model="tagName"
- type="text"
- class="form-control"
- aria-describedby="tag-name-help"
- disabled
- />
- <div id="tag-name-help" class="form-text text-muted">
- {{ __('Choose an existing tag, or create a new one') }}
+ <gl-form-group>
+ <div class="row">
+ <div class="col-md-6 col-lg-5 col-xl-4">
+ <label for="git-ref">{{ __('Tag name') }}</label>
+ <gl-form-input
+ id="git-ref"
+ v-model="tagName"
+ type="text"
+ class="form-control"
+ aria-describedby="tag-name-help"
+ disabled
+ />
</div>
- </gl-form-group>
- </div>
+ </div>
+ <div id="tag-name-help" class="form-text text-muted" v-html="tagNameHintText"></div>
+ </gl-form-group>
<gl-form-group>
<label for="release-title">{{ __('Release title') }}</label>
<gl-form-input
diff --git a/app/assets/javascripts/releases/detail/store/state.js b/app/assets/javascripts/releases/detail/store/state.js
index ff98e2bed78..7e3d975f1ae 100644
--- a/app/assets/javascripts/releases/detail/store/state.js
+++ b/app/assets/javascripts/releases/detail/store/state.js
@@ -4,6 +4,7 @@ export default () => ({
releasesPagePath: null,
markdownDocsPath: null,
markdownPreviewPath: null,
+ updateReleaseApiDocsPath: null,
release: null,
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index de7350f0d2f..d826f209815 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -16,7 +16,6 @@ export default function setupVueRepositoryList() {
const { dataset } = el;
const { projectPath, projectShortPath, ref, fullName } = dataset;
const router = createRouter(projectPath, ref);
- const hideOnRootEls = document.querySelectorAll('.js-hide-on-root');
apolloProvider.clients.defaultClient.cache.writeData({
data: {
@@ -28,20 +27,7 @@ export default function setupVueRepositoryList() {
});
router.afterEach(({ params: { pathMatch } }) => {
- const isRoot = pathMatch === undefined || pathMatch === '/';
-
setTitle(pathMatch, ref, fullName);
-
- if (!isRoot) {
- document
- .querySelectorAll('.js-keep-hidden-on-navigation')
- .forEach(elem => elem.classList.add('hidden'));
- }
-
- document
- .querySelectorAll('.js-hide-on-navigation')
- .forEach(elem => elem.classList.toggle('hidden', !isRoot));
- hideOnRootEls.forEach(elem => elem.classList.toggle('hidden', isRoot));
});
const breadcrumbEl = document.getElementById('js-repo-breadcrumb');
diff --git a/app/assets/javascripts/repository/pages/index.vue b/app/assets/javascripts/repository/pages/index.vue
index 967f4a99281..29786bf4ec8 100644
--- a/app/assets/javascripts/repository/pages/index.vue
+++ b/app/assets/javascripts/repository/pages/index.vue
@@ -1,13 +1,25 @@
<script>
-import TreeContent from '../components/tree_content.vue';
+import TreePage from './tree.vue';
+import { updateElementsVisibility } from '../utils/dom';
export default {
components: {
- TreeContent,
+ TreePage,
+ },
+ mounted() {
+ this.updateProjectElements(true);
+ },
+ beforeDestroy() {
+ this.updateProjectElements(false);
+ },
+ methods: {
+ updateProjectElements(isShow) {
+ updateElementsVisibility('.js-show-on-project-root', isShow);
+ },
},
};
</script>
<template>
- <tree-content />
+ <tree-page path="/" />
</template>
diff --git a/app/assets/javascripts/repository/pages/tree.vue b/app/assets/javascripts/repository/pages/tree.vue
index 19300099449..dd4d437f4dd 100644
--- a/app/assets/javascripts/repository/pages/tree.vue
+++ b/app/assets/javascripts/repository/pages/tree.vue
@@ -1,5 +1,6 @@
<script>
import TreeContent from '../components/tree_content.vue';
+import { updateElementsVisibility } from '../utils/dom';
export default {
components: {
@@ -12,6 +13,23 @@ export default {
default: '/',
},
},
+ computed: {
+ isRoot() {
+ return this.path === '/';
+ },
+ },
+ watch: {
+ isRoot: {
+ immediate: true,
+ handler: 'updateElements',
+ },
+ },
+ methods: {
+ updateElements(isRoot) {
+ updateElementsVisibility('.js-show-on-root', isRoot);
+ updateElementsVisibility('.js-hide-on-root', !isRoot);
+ },
+ },
};
</script>
diff --git a/app/assets/javascripts/repository/utils/dom.js b/app/assets/javascripts/repository/utils/dom.js
new file mode 100644
index 00000000000..963e6fc0bc4
--- /dev/null
+++ b/app/assets/javascripts/repository/utils/dom.js
@@ -0,0 +1,4 @@
+// eslint-disable-next-line import/prefer-default-export
+export const updateElementsVisibility = (selector, isVisible) => {
+ document.querySelectorAll(selector).forEach(elem => elem.classList.toggle('hidden', !isVisible));
+};
diff --git a/app/assets/javascripts/repository/utils/title.js b/app/assets/javascripts/repository/utils/title.js
index 87d54c01200..ff16fbdd420 100644
--- a/app/assets/javascripts/repository/utils/title.js
+++ b/app/assets/javascripts/repository/utils/title.js
@@ -1,10 +1,14 @@
+const DEFAULT_TITLE = '· GitLab';
// eslint-disable-next-line import/prefer-default-export
export const setTitle = (pathMatch, ref, project) => {
- if (!pathMatch) return;
+ if (!pathMatch) {
+ document.title = `${project} ${DEFAULT_TITLE}`;
+ return;
+ }
const path = pathMatch.replace(/^\//, '');
const isEmpty = path === '';
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
- document.title = `${isEmpty ? 'Files' : path} · ${ref} · ${project}`;
+ document.title = `${isEmpty ? 'Files' : path} · ${ref} · ${project} ${DEFAULT_TITLE}`;
};
diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue
index 478e44d104c..f984a0a6203 100644
--- a/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue
+++ b/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue
@@ -1,6 +1,6 @@
<script>
import _ from 'underscore';
-import { GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui';
+import { GlLoadingIcon, GlSearchBoxByType, GlInfiniteScroll } from '@gitlab/ui';
import ProjectListItem from './project_list_item.vue';
const SEARCH_INPUT_TIMEOUT_MS = 500;
@@ -10,6 +10,7 @@ export default {
components: {
GlLoadingIcon,
GlSearchBoxByType,
+ GlInfiniteScroll,
ProjectListItem,
},
props: {
@@ -41,6 +42,11 @@ export default {
required: false,
default: false,
},
+ totalResults: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
},
data() {
return {
@@ -51,6 +57,9 @@ export default {
projectClicked(project) {
this.$emit('projectClicked', project);
},
+ bottomReached() {
+ this.$emit('bottomReached');
+ },
isSelected(project) {
return Boolean(_.find(this.selectedProjects, { id: project.id }));
},
@@ -71,18 +80,25 @@ export default {
@input="onInput"
/>
<div class="d-flex flex-column">
- <gl-loading-icon v-if="showLoadingIndicator" :size="2" class="py-2 px-4" />
- <div v-if="!showLoadingIndicator" class="d-flex flex-column">
- <project-list-item
- v-for="project in projectSearchResults"
- :key="project.id"
- :selected="isSelected(project)"
- :project="project"
- :matcher="searchQuery"
- class="js-project-list-item"
- @click="projectClicked(project)"
- />
- </div>
+ <gl-loading-icon v-if="showLoadingIndicator" :size="1" class="py-2 px-4" />
+ <gl-infinite-scroll
+ :max-list-height="402"
+ :fetched-items="projectSearchResults.length"
+ :total-items="totalResults"
+ @bottomReached="bottomReached"
+ >
+ <div v-if="!showLoadingIndicator" slot="items" class="d-flex flex-column">
+ <project-list-item
+ v-for="project in projectSearchResults"
+ :key="project.id"
+ :selected="isSelected(project)"
+ :project="project"
+ :matcher="searchQuery"
+ class="js-project-list-item"
+ @click="projectClicked(project)"
+ />
+ </div>
+ </gl-infinite-scroll>
<div v-if="showNoResultsMessage" class="text-muted ml-2 js-no-results-message">
{{ __('Sorry, no projects matched your search') }}
</div>
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 120958eb631..8b3d4fbf96a 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -297,9 +297,7 @@ module ApplicationSettingsHelper
:snowplow_iglu_registry_url,
:push_event_hooks_limit,
:push_event_activities_limit,
- :custom_http_clone_url_root,
- :pendo_enabled,
- :pendo_url
+ :custom_http_clone_url_root
]
end
diff --git a/app/helpers/releases_helper.rb b/app/helpers/releases_helper.rb
index 68a19152d8f..c4fe40a0875 100644
--- a/app/helpers/releases_helper.rb
+++ b/app/helpers/releases_helper.rb
@@ -26,7 +26,8 @@ module ReleasesHelper
tag_name: @release.tag,
markdown_preview_path: preview_markdown_path(@project),
markdown_docs_path: help_page_path('user/markdown'),
- releases_page_path: project_releases_path(@project, anchor: @release.tag)
+ releases_page_path: project_releases_path(@project, anchor: @release.tag),
+ update_release_api_docs_path: help_page_path('api/releases/index.md', anchor: 'update-a-release')
}
end
end
diff --git a/app/helpers/repository_languages_helper.rb b/app/helpers/repository_languages_helper.rb
index cf7eee7fff3..7834e86adab 100644
--- a/app/helpers/repository_languages_helper.rb
+++ b/app/helpers/repository_languages_helper.rb
@@ -4,7 +4,7 @@ module RepositoryLanguagesHelper
def repository_languages_bar(languages)
return if languages.none?
- content_tag :div, class: 'progress repository-languages-bar' do
+ content_tag :div, class: 'progress repository-languages-bar js-show-on-project-root' do
safe_join(languages.map { |lang| language_progress(lang) })
end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 6a34f293a4a..c037627570a 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -6,6 +6,12 @@ class ApplicationSetting < ApplicationRecord
include TokenAuthenticatable
include ChronicDurationAttribute
+ # Only remove this >= %12.6 and >= 2019-12-01
+ self.ignored_columns += %i[
+ pendo_enabled
+ pendo_url
+ ]
+
add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
add_authentication_token_field :health_check_access_token
add_authentication_token_field :static_objects_external_storage_auth_token
@@ -103,11 +109,6 @@ class ApplicationSetting < ApplicationRecord
allow_blank: true,
if: :snowplow_enabled
- validates :pendo_url,
- presence: true,
- public_url: true,
- if: :pendo_enabled
-
validates :max_attachment_size,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index 2b2492a793a..77fbe09d4f9 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -135,8 +135,6 @@ module ApplicationSettingImplementation
snowplow_app_id: nil,
snowplow_iglu_registry_url: nil,
custom_http_clone_url_root: nil,
- pendo_enabled: false,
- pendo_url: nil,
productivity_analytics_start_date: Time.now
}
end
diff --git a/app/views/admin/application_settings/integrations.html.haml b/app/views/admin/application_settings/integrations.html.haml
index 519d2bf9bbc..3f459e0f491 100644
--- a/app/views/admin/application_settings/integrations.html.haml
+++ b/app/views/admin/application_settings/integrations.html.haml
@@ -7,5 +7,5 @@
= render_if_exists 'admin/application_settings/slack'
= render 'admin/application_settings/third_party_offers'
= render 'admin/application_settings/snowplow'
-= render_if_exists 'admin/application_settings/pendo'
= render 'admin/application_settings/eks' if Feature.enabled?(:create_eks_clusters)
+
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 0c4c48447c9..0060d8323b0 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -90,4 +90,3 @@
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
= render 'layouts/snowplow'
- = render_if_exists 'layouts/pendo'
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
index 6681bb4d094..20d4084f428 100644
--- a/app/views/projects/_files.html.haml
+++ b/app/views/projects/_files.html.haml
@@ -15,7 +15,7 @@
= render 'shared/commit_well', commit: commit, ref: ref, project: project
- if is_project_overview
- .project-buttons.append-bottom-default{ class: ("js-keep-hidden-on-navigation" if vue_file_list_enabled?) }
+ .project-buttons.append-bottom-default{ class: ("js-show-on-project-root" if vue_file_list_enabled?) }
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
- if vue_file_list_enabled?
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 4783b10cf6d..b7c4114d485 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -3,7 +3,7 @@
- max_project_topic_length = 15
- emails_disabled = @project.emails_disabled?
-.project-home-panel{ class: [("empty-project" if empty_repo), ("js-keep-hidden-on-navigation" if vue_file_list_enabled?)] }
+.project-home-panel{ class: [("empty-project" if empty_repo), ("js-show-on-project-root" if vue_file_list_enabled?)] }
.row.append-bottom-8
.home-panel-title-row.col-md-12.col-lg-6.d-flex
.avatar-container.rect-avatar.s64.home-panel-avatar.append-right-default.float-none
diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index 4f6c7e1f9a6..fef019e1b69 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -1,5 +1,5 @@
- if readme.rich_viewer
- %article.file-holder.readme-holder{ id: 'readme', class: [("limited-width-container" unless fluid_layout), ("js-hide-on-navigation" if vue_file_list_enabled?)] }
+ %article.file-holder.readme-holder{ id: 'readme', class: [("limited-width-container" unless fluid_layout), ("js-show-on-root" if vue_file_list_enabled?)] }
.js-file-title.file-title
= blob_icon readme.mode, readme.name
= link_to project_blob_path(@project, tree_join(@ref, readme.path)) do