diff options
Diffstat (limited to 'app')
19 files changed, 154 insertions, 115 deletions
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index 0932d836589..1638e09132b 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -1,33 +1,32 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, camelcase, one-var-declaration-per-line, no-else-return, max-len */ - import $ from 'jquery'; import { rstrip } from './lib/utils/common_utils'; -window.ConfirmDangerModal = (function() { - function ConfirmDangerModal(form, text) { - var project_path, submit; - this.form = form; - $('.js-confirm-text').text(text || ''); - $('.js-confirm-danger-input').val(''); - $('#modal-confirm-danger').modal('show'); - project_path = $('.js-confirm-danger-match').text(); - submit = $('.js-confirm-danger-submit'); - submit.disable(); - $('.js-confirm-danger-input').off('input'); - $('.js-confirm-danger-input').on('input', function() { - if (rstrip($(this).val()) === project_path) { - return submit.enable(); - } else { - return submit.disable(); - } - }); - $('.js-confirm-danger-submit').off('click'); - $('.js-confirm-danger-submit').on('click', (function(_this) { - return function() { - return _this.form.submit(); - }; - })(this)); - } +function openConfirmDangerModal($form, text) { + $('.js-confirm-text').text(text || ''); + $('.js-confirm-danger-input').val(''); + $('#modal-confirm-danger').modal('show'); + + const confirmTextMatch = $('.js-confirm-danger-match').text(); + const $submit = $('.js-confirm-danger-submit'); + $submit.disable(); + + $('.js-confirm-danger-input').off('input').on('input', function handleInput() { + const confirmText = rstrip($(this).val()); + if (confirmText === confirmTextMatch) { + $submit.enable(); + } else { + $submit.disable(); + } + }); + $('.js-confirm-danger-submit').off('click').on('click', () => $form.submit()); +} - return ConfirmDangerModal; -})(); +export default function initConfirmDangerModal() { + $(document).on('click', '.js-confirm-danger', (e) => { + e.preventDefault(); + const $btn = $(e.target); + const $form = $btn.closest('form'); + const text = $btn.data('confirmDangerMessage'); + openConfirmDangerModal($form, text); + }); +} diff --git a/app/assets/javascripts/ide/components/ide_project_tree.vue b/app/assets/javascripts/ide/components/ide_project_tree.vue index 220db1abfb0..a6f40286ac1 100644 --- a/app/assets/javascripts/ide/components/ide_project_tree.vue +++ b/app/assets/javascripts/ide/components/ide_project_tree.vue @@ -1,13 +1,15 @@ <script> -import projectAvatarImage from '~/vue_shared/components/project_avatar/image.vue'; -import branchesTree from './ide_project_branches_tree.vue'; -import externalLinks from './ide_external_links.vue'; +import ProjectAvatarImage from '~/vue_shared/components/project_avatar/image.vue'; +import Identicon from '../../vue_shared/components/identicon.vue'; +import BranchesTree from './ide_project_branches_tree.vue'; +import ExternalLinks from './ide_external_links.vue'; export default { components: { - branchesTree, - externalLinks, - projectAvatarImage, + BranchesTree, + ExternalLinks, + ProjectAvatarImage, + Identicon, }, props: { project: { @@ -25,7 +27,10 @@ export default { :title="project.name" :href="project.web_url" > - <div class="avatar-container s40 project-avatar"> + <div + v-if="project.avatar_url" + class="avatar-container s40 project-avatar" + > <project-avatar-image class="avatar-container project-avatar" :link-href="project.path" @@ -34,6 +39,12 @@ export default { :img-size="40" /> </div> + <identicon + v-else + size-class="s40" + :entity-id="project.id" + :entity-name="project.name" + /> <div class="sidebar-context-title"> {{ project.name }} </div> diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue index 03a40096bb0..297b9c2628f 100644 --- a/app/assets/javascripts/ide/components/repo_file.vue +++ b/app/assets/javascripts/ide/components/repo_file.vue @@ -43,6 +43,7 @@ export default { 'file-open': this.isBlob && this.file.opened, 'file-active': this.isBlob && this.file.active, folder: this.isTree, + 'is-open': this.file.opened, }; }, }, diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js index 048d5316922..db89c1d44db 100644 --- a/app/assets/javascripts/ide/ide_router.js +++ b/app/assets/javascripts/ide/ide_router.js @@ -54,41 +54,61 @@ const router = new VueRouter({ router.beforeEach((to, from, next) => { if (to.params.namespace && to.params.project) { - store.dispatch('getProjectData', { - namespace: to.params.namespace, - projectId: to.params.project, - }) - .then(() => { - const fullProjectId = `${to.params.namespace}/${to.params.project}`; + store + .dispatch('getProjectData', { + namespace: to.params.namespace, + projectId: to.params.project, + }) + .then(() => { + const fullProjectId = `${to.params.namespace}/${to.params.project}`; - if (to.params.branch) { - store.dispatch('getBranchData', { - projectId: fullProjectId, - branchId: to.params.branch, - }); + if (to.params.branch) { + store.dispatch('getBranchData', { + projectId: fullProjectId, + branchId: to.params.branch, + }); - store.dispatch('getFiles', { - projectId: fullProjectId, - branchId: to.params.branch, - }) - .then(() => { - if (to.params[0]) { - const treeEntry = store.state.entries[to.params[0]]; - if (treeEntry) { - store.dispatch('handleTreeEntryAction', treeEntry); - } - } - }) - .catch((e) => { - flash('Error while loading the branch files. Please try again.', 'alert', document, null, false, true); - throw e; - }); - } - }) - .catch((e) => { - flash('Error while loading the project data. Please try again.', 'alert', document, null, false, true); - throw e; - }); + store + .dispatch('getFiles', { + projectId: fullProjectId, + branchId: to.params.branch, + }) + .then(() => { + if (to.params[0]) { + const path = + to.params[0].slice(-1) === '/' + ? to.params[0].slice(0, -1) + : to.params[0]; + const treeEntry = store.state.entries[path]; + if (treeEntry) { + store.dispatch('handleTreeEntryAction', treeEntry); + } + } + }) + .catch(e => { + flash( + 'Error while loading the branch files. Please try again.', + 'alert', + document, + null, + false, + true, + ); + throw e; + }); + } + }) + .catch(e => { + flash( + 'Error while loading the project data. Please try again.', + 'alert', + document, + null, + false, + true, + ); + throw e; + }); } next(); diff --git a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js index e959130300b..a4cd1ab099f 100644 --- a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js +++ b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js @@ -1,10 +1,14 @@ -import { - decorateData, - sortTree, -} from '../utils'; +import { decorateData, sortTree } from '../utils'; -self.addEventListener('message', (e) => { - const { data, projectId, branchId, tempFile = false, content = '', base64 = false } = e.data; +self.addEventListener('message', e => { + const { + data, + projectId, + branchId, + tempFile = false, + content = '', + base64 = false, + } = e.data; const treeList = []; let file; @@ -15,7 +19,9 @@ self.addEventListener('message', (e) => { if (pathSplit.length > 0) { pathSplit.reduce((pathAcc, folderName) => { const parentFolder = acc[pathAcc[pathAcc.length - 1]]; - const folderPath = `${(parentFolder ? `${parentFolder.path}/` : '')}${folderName}`; + const folderPath = `${ + parentFolder ? `${parentFolder.path}/` : '' + }${folderName}`; const foundEntry = acc[folderPath]; if (!foundEntry) { @@ -25,9 +31,11 @@ self.addEventListener('message', (e) => { id: folderPath, name: folderName, path: folderPath, - url: `/${projectId}/tree/${branchId}/${folderPath}`, + url: `/${projectId}/tree/${branchId}/${folderPath}/`, type: 'tree', - parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`, + parentTreeUrl: parentFolder + ? parentFolder.url + : `/${projectId}/tree/${branchId}/`, tempFile, changed: tempFile, opened: tempFile, @@ -62,7 +70,9 @@ self.addEventListener('message', (e) => { path, url: `/${projectId}/blob/${branchId}/${path}`, type: 'blob', - parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`, + parentTreeUrl: fileFolder + ? fileFolder.url + : `/${projectId}/blob/${branchId}`, tempFile, changed: tempFile, content, diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index cedb6ef19f7..2c80baba10b 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -1,5 +1,4 @@ /* eslint-disable import/first */ -/* global ConfirmDangerModal */ /* global $ */ import jQuery from 'jquery'; @@ -21,7 +20,6 @@ import './behaviors/'; // everything else import loadAwardsHandler from './awards_handler'; import bp from './breakpoints'; -import './confirm_danger_modal'; import Flash, { removeFlashClickListener } from './flash'; import './gl_dropdown'; import initTodoToggle from './header'; @@ -214,16 +212,6 @@ document.addEventListener('DOMContentLoaded', () => { $(document).trigger('toggle.comments'); }); - $document.on('click', '.js-confirm-danger', (e) => { - const btn = $(e.target); - const form = btn.closest('form'); - const text = btn.data('confirmDangerMessage'); - e.preventDefault(); - - // eslint-disable-next-line no-new - new ConfirmDangerModal(form, text); - }); - $document.on('breakpoint:change', (e, breakpoint) => { if (breakpoint === 'sm' || breakpoint === 'xs') { const $gutterIcon = $sidebarGutterToggle.find('i'); diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js index d44874c8741..bb91ac84ffb 100644 --- a/app/assets/javascripts/pages/groups/edit/index.js +++ b/app/assets/javascripts/pages/groups/edit/index.js @@ -1,7 +1,9 @@ import groupAvatar from '~/group_avatar'; import TransferDropdown from '~/groups/transfer_dropdown'; +import initConfirmDangerModal from '~/confirm_danger_modal'; document.addEventListener('DOMContentLoaded', () => { groupAvatar(); new TransferDropdown(); // eslint-disable-line no-new + initConfirmDangerModal(); }); diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js index 064de22dfd6..be37df36be8 100644 --- a/app/assets/javascripts/pages/projects/edit/index.js +++ b/app/assets/javascripts/pages/projects/edit/index.js @@ -1,5 +1,6 @@ import initSettingsPanels from '~/settings_panels'; import setupProjectEdit from '~/project_edit'; +import initConfirmDangerModal from '~/confirm_danger_modal'; import ProjectNew from '../shared/project_new'; import projectAvatar from '../shared/project_avatar'; import initProjectPermissionsSettings from '../shared/permissions'; @@ -11,4 +12,5 @@ document.addEventListener('DOMContentLoaded', () => { initSettingsPanels(); projectAvatar(); initProjectPermissionsSettings(); + initConfirmDangerModal(); }); diff --git a/app/assets/javascripts/vue_shared/components/file_icon.vue b/app/assets/javascripts/vue_shared/components/file_icon.vue index c9d7c0f4999..ee1c3498748 100644 --- a/app/assets/javascripts/vue_shared/components/file_icon.vue +++ b/app/assets/javascripts/vue_shared/components/file_icon.vue @@ -62,8 +62,7 @@ return `${gon.sprite_file_icons}#${iconName}`; }, folderIconName() { - // We don't have a open folder icon yet - return this.opened ? 'folder' : 'folder'; + return this.opened ? 'folder-open' : 'folder'; }, iconSizeClass() { return this.size ? `s${this.size}` : ''; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 127583626cf..6397757bf88 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -501,10 +501,8 @@ -moz-osx-font-smoothing: grayscale; } - &.dropdown-menu-user-link { - &::before { - top: 50%; - } + &.dropdown-menu-user-link::before { + top: 50%; } } diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index f6ddb6d4cfe..6d6b840f485 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -377,4 +377,11 @@ module IssuablesHelper def parent @project || @group end + + def issuable_milestone_tooltip_title(issuable) + if issuable.milestone + milestone_tooltip = milestone_tooltip_title(issuable.milestone) + _('Milestone') + (milestone_tooltip ? ': ' + milestone_tooltip : '') + end + end end diff --git a/app/models/group.rb b/app/models/group.rb index 8d183006c65..f669b1a7009 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -230,13 +230,13 @@ class Group < Namespace end GroupMember - .active_without_invites + .active_without_invites_and_requests .where(source_id: source_ids) end def members_with_descendants GroupMember - .active_without_invites + .active_without_invites_and_requests .where(source_id: self_and_descendants.reorder(nil).select(:id)) end diff --git a/app/models/member.rb b/app/models/member.rb index ec8156bbb01..e1a32148538 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -52,7 +52,7 @@ class Member < ActiveRecord::Base end # Like active, but without invites. For when a User is required. - scope :active_without_invites, -> do + scope :active_without_invites_and_requests, -> do left_join_users .where(users: { state: 'active' }) .non_request diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index ab94db2c1e5..d7d2cde1004 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -208,9 +208,9 @@ class NotificationService def new_access_request(member) return true unless member.notifiable?(:subscription) - recipients = member.source.members.active_without_invites.owners_and_masters + recipients = member.source.members.active_without_invites_and_requests.owners_and_masters if fallback_to_group_owners_masters?(recipients, member) - recipients = member.source.group.members.active_without_invites.owners_and_masters + recipients = member.source.group.members.active_without_invites_and_requests.owners_and_masters end recipients.each { |recipient| deliver_access_request_email(recipient, member) } diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 64c648f201b..0c58dd60e2c 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -7,7 +7,9 @@ .issue-main-info .issue-title.title %span.issue-title-text - = confidential_icon(issue) + - if issue.confidential? + %span.has-tooltip{ title: _('Confidential') } + = confidential_icon(issue) = link_to issue.title, issue_path(issue) - if issue.tasks? %span.task-status.hidden-xs @@ -24,11 +26,11 @@ - if issue.milestone %span.issuable-milestone.hidden-xs - = link_to project_issues_path(issue.project, milestone_title: issue.milestone.title), data: { html: 1, toggle: 'tooltip', title: milestone_tooltip_title(issue.milestone) } do + = link_to project_issues_path(issue.project, milestone_title: issue.milestone.title), data: { html: 1, toggle: 'tooltip', title: issuable_milestone_tooltip_title(issue) } do = icon('clock-o') = issue.milestone.title - if issue.due_date - %span.issuable-due-date.hidden-xs{ class: "#{'cred' if issue.overdue?}" } + %span.issuable-due-date.hidden-xs.has-tooltip{ class: "#{'cred' if issue.overdue?}", title: _('Due date') } = icon('calendar') = issue.due_date.to_s(:medium) diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index f45a000833b..a94267deeb2 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -23,11 +23,11 @@ - if merge_request.milestone %span.issuable-milestone.hidden-xs - = link_to project_merge_requests_path(merge_request.project, milestone_title: merge_request.milestone.title), data: { html: 1, toggle: 'tooltip', title: milestone_tooltip_title(merge_request.milestone) } do + = link_to project_merge_requests_path(merge_request.project, milestone_title: merge_request.milestone.title), data: { html: 1, toggle: 'tooltip', title: issuable_milestone_tooltip_title(merge_request) } do = icon('clock-o') = merge_request.milestone.title - if merge_request.target_project.default_branch != merge_request.target_branch - %span.project-ref-path + %span.project-ref-path.has-tooltip{ title: _('Target branch') } = link_to project_ref_path(merge_request.project, merge_request.target_branch), class: 'ref-name' do = sprite_icon('fork', size: 12, css_class: 'fork-sprite') @@ -51,11 +51,11 @@ = render_pipeline_status(merge_request.head_pipeline) - if merge_request.open? && merge_request.broken? %li.issuable-pipeline-broken.hidden-xs - = link_to merge_request_path(merge_request), class: "has-tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do + = link_to merge_request_path(merge_request), class: "has-tooltip", title: _('Cannot be merged automatically') do = icon('exclamation-triangle') - if merge_request.assignee %li - = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name") + = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: _('Assigned to :name')) = render 'shared/issuable_meta_data', issuable: merge_request diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 67613949b7d..5ef5e9c09a2 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -77,7 +77,7 @@ = render 'projects/find_file_link' = succeed " " do - = link_to ide_edit_path(@project, @id), class: 'btn btn-default' do + = link_to ide_edit_path(@project, @id, ""), class: 'btn btn-default' do = _('Web IDE') = render 'projects/buttons/download', project: @project, ref: @ref diff --git a/app/views/shared/_issuable_meta_data.html.haml b/app/views/shared/_issuable_meta_data.html.haml index 435acbc634c..430d9a9dd76 100644 --- a/app/views/shared/_issuable_meta_data.html.haml +++ b/app/views/shared/_issuable_meta_data.html.haml @@ -5,21 +5,21 @@ - issuable_mr = @issuable_meta_data[issuable.id].merge_requests_count - if issuable_mr > 0 - %li.issuable-mr.hidden-xs + %li.issuable-mr.hidden-xs.has-tooltip{ title: _('Related merge requests') } = image_tag('icon-merge-request-unmerged.svg', class: 'icon-merge-request-unmerged') = issuable_mr - if upvotes > 0 - %li.issuable-upvotes.hidden-xs + %li.issuable-upvotes.hidden-xs.has-tooltip{ title: _('Upvotes') } = icon('thumbs-up') = upvotes - if downvotes > 0 - %li.issuable-downvotes.hidden-xs + %li.issuable-downvotes.hidden-xs.has-tooltip{ title: _('Downvotes') } = icon('thumbs-down') = downvotes %li.issuable-comments.hidden-xs - = link_to issuable_url, class: ('no-comments' if note_count.zero?) do + = link_to issuable_url, class: ['has-tooltip', ('no-comments' if note_count.zero?)], title: _('Comments') do = icon('comments') = note_count diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml index 2e9ad380012..149bf8da4b9 100644 --- a/app/views/shared/boards/components/_board.html.haml +++ b/app/views/shared/boards/components/_board.html.haml @@ -4,7 +4,7 @@ %header.board-header{ ":class" => '{ "has-border": list.label && list.label.color }', ":style" => "{ borderTopColor: (list.label && list.label.color ? list.label.color : null) }", "@click" => "toggleExpanded($event)" } %h3.board-title.js-board-handle{ ":class" => '{ "user-can-drag": (!disabled && !list.preset) }' } %i.fa.fa-fw.board-title-expandable-toggle{ "v-if": "list.isExpandable", - ":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded && list.position === -1, \"fa-caret-left\": !list.isExpanded && list.position !== -1 }", + ":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded }", "aria-hidden": "true" } %span.board-title-text.has-tooltip{ "v-if": "list.type !== \"label\"", |