diff options
author | Phil Hughes <me@iamphill.com> | 2017-01-17 08:34:07 -0500 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-01-17 08:34:07 -0500 |
commit | c0ba747c586ff7f5f42097a0e49eace30c57ebdf (patch) | |
tree | 89b58aa0b6162f4aee0bd8f834cc65e12e6c6c85 /app | |
parent | dc623cf44e691cfe3e53decf66e36c25394dadb3 (diff) | |
parent | e7fdb1aae5a61b30f66ea3489d4e0759ed8ea3a1 (diff) | |
download | gitlab-ce-c0ba747c586ff7f5f42097a0e49eace30c57ebdf.tar.gz |
Merge branch 'master' into go-go-gadget-webpack
Diffstat (limited to 'app')
26 files changed, 218 insertions, 81 deletions
diff --git a/app/assets/javascripts/diff.js.es6 b/app/assets/javascripts/diff.js.es6 index 9cf33e62958..5e1a4c948aa 100644 --- a/app/assets/javascripts/diff.js.es6 +++ b/app/assets/javascripts/diff.js.es6 @@ -20,7 +20,7 @@ .on('click', '.js-unfold', this.handleClickUnfold.bind(this)) .on('click', '.diff-line-num a', this.handleClickLineNum.bind(this)); - this.highlighSelectedLine(); + this.openAnchoredDiff(); } handleClickUnfold(e) { @@ -61,13 +61,22 @@ $.get(link, params, response => $target.parent().replaceWith(response)); } - openAnchoredDiff(anchoredDiff, cb) { - const diffTitle = $(`#file-path-${anchoredDiff}`); + openAnchoredDiff(cb) { + const locationHash = gl.utils.getLocationHash(); + const anchoredDiff = locationHash && locationHash.split('_')[0]; + + if (!anchoredDiff) return; + + const diffTitle = $(`#${anchoredDiff}`); const diffFile = diffTitle.closest('.diff-file'); const nothingHereBlock = $('.nothing-here-block:visible', diffFile); if (nothingHereBlock.length) { - diffFile.singleFileDiff(true, cb); - } else { + const clickTarget = $('.file-title, .click-to-expand', diffFile); + diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => { + this.highlighSelectedLine(); + if (cb) cb(); + }); + } else if (cb) { cb(); } } diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index a669d8bd4e2..df660a9a300 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -476,7 +476,7 @@ this.removeArrayKeyEvent(); $input = this.dropdown.find(".dropdown-input-field"); if (this.options.filterable) { - $input.blur().val(""); + $input.blur(); } if (this.dropdown.find(".dropdown-toggle-page").length) { $('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS); diff --git a/app/assets/javascripts/merge_request_tabs.js.es6 b/app/assets/javascripts/merge_request_tabs.js.es6 index b8ab00458de..e8577c87d99 100644 --- a/app/assets/javascripts/merge_request_tabs.js.es6 +++ b/app/assets/javascripts/merge_request_tabs.js.es6 @@ -237,13 +237,8 @@ require('./flash'); } this.diffsLoaded = true; - const diffPage = new gl.Diff(); - - const locationHash = gl.utils.getLocationHash(); - const anchoredDiff = locationHash && locationHash.split('_')[0]; - if (anchoredDiff) { - diffPage.openAnchoredDiff(anchoredDiff, () => this.scrollToElement('#diffs')); - } + new gl.Diff(); + this.scrollToElement('#diffs'); }, }); } diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js index 4b6ebadeac7..5945cab4cf0 100644 --- a/app/assets/javascripts/search.js +++ b/app/assets/javascripts/search.js @@ -12,6 +12,9 @@ selectable: true, filterable: true, fieldName: 'group_id', + search: { + fields: ['name'] + }, data: function(term, callback) { return Api.groups(term, {}, function(data) { data.unshift({ @@ -40,6 +43,9 @@ selectable: true, filterable: true, fieldName: 'project_id', + search: { + fields: ['name'] + }, data: function(term, callback) { return Api.projects(term, 'id', function(data) { data.unshift({ diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index ac8603ccd10..9602526063e 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, padded-blocks, max-len */ +/* eslint-disable func-names, prefer-arrow-callback, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; @@ -14,8 +14,7 @@ COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>'; - function SingleFileDiff(file, forceLoad, cb) { - var clickTarget; + function SingleFileDiff(file) { this.file = file; this.toggleDiff = bind(this.toggleDiff, this); this.content = $('.diff-content', this.file); @@ -33,14 +32,13 @@ this.content.after(this.collapsedContent); this.$toggleIcon.addClass('fa-caret-down'); } - clickTarget = $('.file-title, .click-to-expand', this.file).on('click', this.toggleDiff); - if (forceLoad) { - this.toggleDiff({ target: clickTarget }, cb); - } + + $('.file-title, .click-to-expand', this.file).on('click', (function (e) { + this.toggleDiff($(e.target)); + }).bind(this)); } - SingleFileDiff.prototype.toggleDiff = function(e, cb) { - var $target = $(e.target); + SingleFileDiff.prototype.toggleDiff = function($target, cb) { if (!$target.hasClass('file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return; this.isOpen = !this.isOpen; if (!this.isOpen && !this.hasError) { @@ -91,10 +89,10 @@ })(); - $.fn.singleFileDiff = function(forceLoad, cb) { + $.fn.singleFileDiff = function() { return this.each(function() { - if (!$.data(this, 'singleFileDiff') || forceLoad) { - return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this, forceLoad, cb)); + if (!$.data(this, 'singleFileDiff')) { + return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this)); } }); }; diff --git a/app/assets/javascripts/vue_pipelines_index/stage.js.es6 b/app/assets/javascripts/vue_pipelines_index/stage.js.es6 index 74a79dcedae..f075a995846 100644 --- a/app/assets/javascripts/vue_pipelines_index/stage.js.es6 +++ b/app/assets/javascripts/vue_pipelines_index/stage.js.es6 @@ -1,11 +1,11 @@ /* global Vue, Flash, gl */ -/* eslint-disable no-param-reassign */ +/* eslint-disable no-param-reassign, no-bitwise */ ((gl) => { gl.VueStage = Vue.extend({ data() { return { - request: false, + count: 0, builds: '', spinner: '<span class="fa fa-spinner fa-spin"></span>', }; @@ -13,29 +13,23 @@ props: ['stage', 'svgs', 'match'], methods: { fetchBuilds() { - if (this.request) return this.clearBuilds(); - + if (this.count > 0) return null; return this.$http.get(this.stage.dropdown_path) .then((response) => { - this.request = true; + this.count += 1; this.builds = JSON.parse(response.body).html; }, () => { const flash = new Flash('Something went wrong on our end.'); - this.request = false; return flash; }); }, - clearBuilds() { - this.builds = ''; - this.request = false; - }, }, computed: { buildsOrSpinner() { - return this.request ? this.builds : this.spinner; + return this.builds ? this.builds : this.spinner; }, dropdownClass() { - if (this.request) return 'js-builds-dropdown-container'; + if (this.builds) return 'js-builds-dropdown-container'; return 'js-builds-dropdown-loading builds-dropdown-loading'; }, buildStatus() { @@ -57,7 +51,6 @@ <div> <button @click='fetchBuilds' - @blur='fetchBuilds' :class="triggerButtonClass" :title='stage.title' data-placement="top" diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index f1d36efb3de..8d38fc78a19 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -50,3 +50,77 @@ .pulse { @include webkit-prefix(animation-name, pulse); } + +/* +* General hover animations +*/ + + +// Sass multiple transitions mixin | https://gist.github.com/tobiasahlin/7a421fb9306a4f518aab +// Usage: @include transition(width, height 0.3s ease-in-out); +// Output: -webkit-transition(width 0.2s, height 0.3s ease-in-out); +// transition(width 0.2s, height 0.3s ease-in-out); +// +// Pass in any number of transitions +@mixin transition($transitions...) { + $unfoldedTransitions: (); + @each $transition in $transitions { + $unfoldedTransitions: append($unfoldedTransitions, unfoldTransition($transition), comma); + } + + transition: $unfoldedTransitions; +} + +@function unfoldTransition ($transition) { + // Default values + $property: all; + $duration: $general-hover-transition-duration; + $easing: $general-hover-transition-curve; // Browser default is ease, which is what we want + $delay: null; // Browser default is 0, which is what we want + $defaultProperties: ($property, $duration, $easing, $delay); + + // Grab transition properties if they exist + $unfoldedTransition: (); + @for $i from 1 through length($defaultProperties) { + $p: null; + @if $i <= length($transition) { + $p: nth($transition, $i); + } @else { + $p: nth($defaultProperties, $i); + } + $unfoldedTransition: append($unfoldedTransition, $p); + } + + @return $unfoldedTransition; +} + +.btn, +.side-nav-toggle { + @include transition(background-color, border-color, color, box-shadow); +} + +.dropdown-menu-toggle, +.avatar-circle, +.header-user-avatar { + @include transition(border-color); +} + +.note-action-button .link-highlight, +.toolbar-btn, +.dropdown-toggle-caret, +.fa:not(.fa-bell) { + @include transition(color); +} + +a { + @include transition(background-color, color, border); +} + +.tree-table td, +.well-list > li { + @include transition(background-color, border-color); +} + +.stage-nav-item { + @include transition(background-color, box-shadow); +} diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index 48827578d94..8392b98f0a7 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -52,6 +52,10 @@ border-radius: 0; border: none; } + + &:not([href]):hover { + border-color: rgba($avatar-border, .2); + } } .identicon { diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 72b3fe2016c..24a1ce2b84d 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -57,6 +57,14 @@ header { &.header-user-dropdown-toggle { margin-left: 14px; + + &:hover, + &:focus, + &:active { + .header-user-avatar { + border-color: rgba($avatar-border, .2); + } + } } &:hover, @@ -104,6 +112,7 @@ header { &:hover { background-color: $white-normal; + color: $gl-header-nav-hover-color; } } } @@ -180,6 +189,7 @@ header { &:hover { text-decoration: underline; + color: $gl-header-nav-hover-color; } } @@ -198,7 +208,7 @@ header { cursor: pointer; &:hover { - color: darken($color: $gl-text-color, $amount: 30%); + color: $gl-header-nav-hover-color; } } @@ -271,4 +281,5 @@ header { float: left; margin-right: 5px; border-radius: 50%; + border: 1px solid $avatar-border; } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index a292e7686f9..401c2d0f6ee 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -101,7 +101,7 @@ &:hover, &:active, &:focus { - border-bottom: none; + border-color: transparent; } } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index cf9424ea5dd..349cd9c189e 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -102,6 +102,10 @@ $gl-text-red: #d12f19; $gl-text-orange: #d90; $gl-link-color: #3777b0; $gl-grayish-blue: #7f8fa4; +$gl-gray: $gl-text-color; +$gl-gray-dark: #313236; +$gl-header-color: #4c4e54; +$gl-header-nav-hover-color: #434343; /* * Lists @@ -172,6 +176,9 @@ $count-arrow-border: #dce0e5; $save-project-loader-color: #555; $divergence-graph-bar-bg: #ccc; $divergence-graph-separator-bg: #ccc; +$general-hover-transition-duration: 150ms; +$general-hover-transition-curve: linear; + /* * Common component specific colors @@ -530,4 +537,4 @@ Pipeline Graph */ $stage-hover-bg: #eaf3fc; $stage-hover-border: #d1e7fc; -$action-icon-color: #d6d6d6; +$action-icon-color: #d6d6d6;
\ No newline at end of file diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index 6566f27ea2d..cda069e6c0e 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -20,6 +20,10 @@ .fa { color: $cycle-analytics-light-gray; + + &:hover { + color: $gl-text-color; + } } .stage-header { diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 3272a862b85..0ae5dc5c537 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -154,8 +154,8 @@ .edit-link { color: $gl-text-color; - &:hover { - color: $md-link-color; + &:not([href]):hover { + color: rgba($avatar-border, .2); } } } @@ -332,6 +332,10 @@ &:hover { color: $md-link-color; text-decoration: none; + + .avatar { + border-color: rgba($avatar-border, .2); + } } } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 78683c7d574..21d9b4c54ea 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -203,6 +203,10 @@ z-index: 3; border-radius: $label-border-radius; padding: 6px 10px 6px 9px; + + &:hover { + box-shadow: inset 0 0 0 80px $label-remove-border; + } } .btn { diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 8b1976bd925..722b3006f7c 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -216,8 +216,8 @@ } } -.user-profile { +.user-profile { .cover-controls a { margin-left: 5px; } @@ -231,8 +231,11 @@ } } - @media (max-width: $screen-xs-max) { + .user-profile-nav { + font-size: 0; + } + @media (max-width: $screen-xs-max) { .cover-block { padding-top: 20px; } @@ -253,6 +256,12 @@ } } } + + .user-profile-nav { + a { + margin-right: 0; + } + } } } @@ -271,4 +280,4 @@ table.u2f-registrations { .scopes-list { padding-left: 18px; } -}
\ No newline at end of file +} diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index cedd4cb2987..12bff32bbf3 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -14,6 +14,20 @@ } } +.search form:hover, +.file-finder-input:hover, +.issuable-search-form:hover, +.search-text-input:hover, +textarea:hover, +.form-control:hover { + border-color: lighten($dropdown-input-focus-border, 20%); + box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%); +} + +input[type="checkbox"]:hover { + box-shadow: 0 0 2px 2px lighten($search-input-focus-shadow-color, 20%), 0 0 0 1px lighten($search-input-focus-shadow-color, 20%); +} + .search { margin-right: 10px; margin-left: 10px; diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index d5ee503c44c..444ff837bb3 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -42,19 +42,16 @@ class ProjectsController < Projects::ApplicationController end def update - status = ::Projects::UpdateService.new(@project, current_user, project_params).execute + result = ::Projects::UpdateService.new(@project, current_user, project_params).execute # Refresh the repo in case anything changed - @repository = project.repository + @repository = @project.repository respond_to do |format| - if status + if result[:status] == :success flash[:notice] = "Project '#{@project.name}' was successfully updated." format.html do - redirect_to( - edit_project_path(@project), - notice: "Project '#{@project.name}' was successfully updated." - ) + redirect_to(edit_project_path(@project)) end else format.html { render 'edit' } diff --git a/app/models/project.rb b/app/models/project.rb index e85d3d3bc6c..1630975b0d3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1032,7 +1032,7 @@ class Project < ActiveRecord::Base "refs/heads/#{branch}", force: true) repository.copy_gitattributes(branch) - repository.expire_avatar_cache + repository.after_change_head reload_default_branch end diff --git a/app/models/repository.rb b/app/models/repository.rb index 3266e9c75f0..43dba86e5ed 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -439,6 +439,11 @@ class Repository expire_content_cache end + # Runs code after the HEAD of a repository is changed. + def after_change_head + expire_method_caches(METHOD_CACHES_FOR_FILE_TYPES.keys) + end + # Runs code after a repository has been forked/imported. def after_import expire_content_cache diff --git a/app/serializers/build_action_entity.rb b/app/serializers/build_action_entity.rb index 3e72892d584..184f5fd4b52 100644 --- a/app/serializers/build_action_entity.rb +++ b/app/serializers/build_action_entity.rb @@ -2,7 +2,7 @@ class BuildActionEntity < Grape::Entity include RequestAwareEntity expose :name do |build| - build.name.humanize + build.name end expose :path do |build| diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 8a6af8d8ada..842e23eb6b6 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -9,7 +9,7 @@ module Projects Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) deny_visibility_level(project, new_visibility) - return project + return error('Visibility level unallowed') end end @@ -23,6 +23,10 @@ module Projects if project.previous_changes.include?('path') project.rename_repo end + + success + else + error('Project could not be updated') end end end diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb index 21ec1bd9e65..2d211d5ebbe 100644 --- a/app/services/users/refresh_authorized_projects_service.rb +++ b/app/services/users/refresh_authorized_projects_service.rb @@ -26,8 +26,26 @@ module Users user.reload end - # This method returns the updated User object. def execute + lease_key = "refresh_authorized_projects:#{user.id}" + lease = Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT) + + until uuid = lease.try_obtain + # Keep trying until we obtain the lease. If we don't do so we may end up + # not updating the list of authorized projects properly. To prevent + # hammering Redis too much we'll wait for a bit between retries. + sleep(1) + end + + begin + execute_without_lease + ensure + Gitlab::ExclusiveLease.cancel(lease_key, uuid) + end + end + + # This method returns the updated User object. + def execute_without_lease current = current_authorizations_per_project fresh = fresh_access_levels_per_project @@ -47,26 +65,7 @@ module Users end end - update_with_lease(remove, add) - end - - # Updates the list of authorizations using an exclusive lease. - def update_with_lease(remove = [], add = []) - lease_key = "refresh_authorized_projects:#{user.id}" - lease = Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT) - - until uuid = lease.try_obtain - # Keep trying until we obtain the lease. If we don't do so we may end up - # not updating the list of authorized projects properly. To prevent - # hammering Redis too much we'll wait for a bit between retries. - sleep(1) - end - - begin - update_authorizations(remove, add) - ensure - Gitlab::ExclusiveLease.cancel(lease_key, uuid) - end + update_authorizations(remove, add) end # Updates the list of authorizations for the current user. diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 2385a90401e..c0c82cde2f6 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -18,7 +18,8 @@ or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host} .col-lg-9 .clearfix.avatar-image.append-bottom-default - = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160' + = link_to avatar_icon(@user, 400), target: '_blank' do + = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160' %h5.prepend-top-0 Upload new avatar .prepend-top-5.append-bottom-10 diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 2eb49685f08..04efc2e996c 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -17,7 +17,6 @@ - if @project.protected_branch? branch.name %span.label.label-success - %i.fa.fa-lock protected .controls.hidden-xs - if merge_project && create_mr_button?(@repository.root_ref, branch.name) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 6ce586cc8f6..990bfbcf951 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -86,7 +86,7 @@ %li = link_to play_namespace_project_build_path(pipeline.project.namespace, pipeline.project, build), method: :post, rel: 'nofollow' do = custom_icon('icon_play') - %span= build.name.humanize + %span= build.name - if artifacts.present? .btn-group %button.dropdown-toggle.btn.btn-default.build-artifacts.js-pipeline-dropdown-download{ type: 'button', 'data-toggle' => 'dropdown' } diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 15df2edefc7..c37a33bbcd5 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,5 +1,5 @@ .diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id) } - .file-title{ id: "file-path-#{hexdigest(diff_file.file_path)}" } + .file-title = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}" - unless diff_file.submodule? |