diff options
author | Stan Hu <stanhu@gmail.com> | 2019-08-08 04:40:55 +0000 |
---|---|---|
committer | Stan Hu <stanhu@gmail.com> | 2019-08-08 04:40:55 +0000 |
commit | 3ad34c3a243cda19e73fbda0c9682e0a1c16c10a (patch) | |
tree | 38a419d054a0c999449d7837a311e28364e0cc42 /app | |
parent | 4985f6e26d758e7d5e43c3525a4afe4a9459ce8e (diff) | |
parent | d4078b535c9854695e770cdfb5e0f4846a8cf64a (diff) | |
download | gitlab-ce-3ad34c3a243cda19e73fbda0c9682e0a1c16c10a.tar.gz |
Merge branch '20137-starrers' into 'master'
Add possibilty to view starrers ("stargazers") of a repository & any user's starred repositories
Closes #20137
See merge request gitlab-org/gitlab-ce!24690
Diffstat (limited to 'app')
22 files changed, 372 insertions, 70 deletions
diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index 1d8b388e935..4ac4efec45d 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -143,7 +143,7 @@ export default class UserTabs { this.loadOverviewTab(); } - const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; + const loadableActions = ['groups', 'contributed', 'projects', 'starred', 'snippets']; if (loadableActions.indexOf(action) > -1) { this.loadTab(action, endpoint); } diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js index 70f89152f70..97afeecd8ac 100644 --- a/app/assets/javascripts/star.js +++ b/app/assets/javascripts/star.js @@ -18,7 +18,7 @@ export default class Star { const isStarred = $starSpan.hasClass('starred'); $this .parent() - .find('.star-count') + .find('.count') .text(data.star_count); if (isStarred) { diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 68af01f9ccc..d54d264bc5d 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -9,10 +9,6 @@ } } -.member-sort-dropdown { - margin-left: $gl-padding-8; -} - .member { &.is-overridden { .btn-ldap-override { @@ -62,36 +58,9 @@ } } -.member-search-form { - position: relative; - - @include media-breakpoint-up(sm) { - float: right; - } - - .dropdown { - width: 100%; - margin-top: 5px; - - .dropdown-menu-toggle { - vertical-align: middle; - width: 100%; - } - - @include media-breakpoint-up(sm) { - margin-top: 0; - width: 155px; - } - } - - .form-control { - width: 100%; - padding-right: 35px; - - @include media-breakpoint-up(sm) { - width: 250px; - } - } +.member-access-text { + margin-left: auto; + line-height: 43px; } .member-search-btn { @@ -177,7 +146,7 @@ padding-bottom: 1px; } - .flex-project-members-form { + .flex-users-form { flex-wrap: nowrap; white-space: nowrap; margin-left: auto; diff --git a/app/assets/stylesheets/pages/users.scss b/app/assets/stylesheets/pages/users.scss new file mode 100644 index 00000000000..3b018c1e087 --- /dev/null +++ b/app/assets/stylesheets/pages/users.scss @@ -0,0 +1,105 @@ +.user-sort-dropdown { + margin-left: $gl-padding-8; +} + +.user-search-form { + position: relative; + + @include media-breakpoint-up(sm) { + float: right; + } + + .dropdown { + width: 100%; + margin-top: 5px; + + .dropdown-menu-toggle { + vertical-align: middle; + width: 100%; + } + + @include media-breakpoint-up(sm) { + margin-top: 0; + width: 155px; + } + } + + .form-control { + width: 100%; + padding-right: 35px; + + @include media-breakpoint-up(sm) { + width: 250px; + } + } +} + +.user-search-btn { + position: absolute; + right: 4px; + top: 0; + height: 35px; + padding-left: 10px; + padding-right: 10px; + color: $gray-darkest; + background: transparent; + border: 0; + outline: 0; +} + +.flex-users-panel { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + + @include media-breakpoint-down(sm) { + display: block; + + .flex-project-title { + vertical-align: top; + display: inline-block; + max-width: 90%; + } + } + + .flex-project-title { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .badge.badge-pill { + height: 17px; + line-height: 16px; + margin-right: 5px; + padding-top: 1px; + padding-bottom: 1px; + } + + .flex-users-form { + flex-wrap: nowrap; + white-space: nowrap; + margin-left: auto; + } +} + +.content-list.members-list li { + display: flex; + justify-content: space-between; + + .list-item-name { + float: none; + display: flex; + flex: 1; + } +} + +.card-body .user-info { + float: left; + + .user { + color: $gl-text-color; + font-weight: $gl-font-weight-bold; + } +} diff --git a/app/controllers/projects/starrers_controller.rb b/app/controllers/projects/starrers_controller.rb new file mode 100644 index 00000000000..c8facea1d70 --- /dev/null +++ b/app/controllers/projects/starrers_controller.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class Projects::StarrersController < Projects::ApplicationController + include SortingHelper + + def index + @starrers = UsersStarProjectsFinder.new(@project, params, current_user: @current_user).execute + + # Normally the number of public starrers is equal to the number of visible + # starrers. We need to fix the counts in two cases: when the current user + # is an admin (and can see everything) and when the current user has a + # private profile and has starred the project (and can see itself). + @public_count = + if @current_user&.admin? + @starrers.with_public_profile.count + elsif @current_user&.private_profile && has_starred_project?(@starrers) + @starrers.size - 1 + else + @starrers.size + end + + @total_count = @project.starrers.size + @private_count = @total_count - @public_count + + @sort = params[:sort].presence || sort_value_name + @starrers = @starrers.sort_by_attribute(@sort).page(params[:page]) + end + + private + + def has_starred_project?(starrers) + starrers.first { |starrer| starrer.user_id == current_user.id } + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 072d62ddf38..91e0efcf45f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -17,7 +17,7 @@ class UsersController < ApplicationController prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) } before_action :user, except: [:exists] before_action :authorize_read_user_profile!, - only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :snippets] + only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :starred_projects, :snippets] def show respond_to do |format| @@ -57,27 +57,30 @@ class UsersController < ApplicationController def projects load_projects - skip_pagination = Gitlab::Utils.to_boolean(params[:skip_pagination]) - skip_namespace = Gitlab::Utils.to_boolean(params[:skip_namespace]) - compact_mode = Gitlab::Utils.to_boolean(params[:compact_mode]) - - respond_to do |format| - format.html { render 'show' } - format.json do - pager_json("shared/projects/_list", @projects.count, projects: @projects, skip_pagination: skip_pagination, skip_namespace: skip_namespace, compact_mode: compact_mode) - end - end + present_projects(@projects) end def contributed load_contributed_projects + present_projects(@contributed_projects) + end + + def starred + load_starred_projects + + present_projects(@starred_projects) + end + + def present_projects(projects) + skip_pagination = Gitlab::Utils.to_boolean(params[:skip_pagination]) + skip_namespace = Gitlab::Utils.to_boolean(params[:skip_namespace]) + compact_mode = Gitlab::Utils.to_boolean(params[:compact_mode]) + respond_to do |format| format.html { render 'show' } format.json do - render json: { - html: view_to_html_string("shared/projects/_list", projects: @contributed_projects) - } + pager_json("shared/projects/_list", projects.count, projects: projects, skip_pagination: skip_pagination, skip_namespace: skip_namespace, compact_mode: compact_mode) end end end @@ -120,6 +123,10 @@ class UsersController < ApplicationController ContributedProjectsFinder.new(user).execute(current_user) end + def starred_projects + StarredProjectsFinder.new(user, current_user: current_user).execute + end + def contributions_calendar @contributions_calendar ||= Gitlab::ContributionsCalendar.new(user, current_user) end @@ -145,6 +152,12 @@ class UsersController < ApplicationController prepare_projects_for_rendering(@contributed_projects) end + def load_starred_projects + @starred_projects = starred_projects + + prepare_projects_for_rendering(@starred_projects) + end + def load_groups @groups = JoinedGroupsFinder.new(user).execute(current_user) diff --git a/app/finders/starred_projects_finder.rb b/app/finders/starred_projects_finder.rb new file mode 100644 index 00000000000..fcb469d1d17 --- /dev/null +++ b/app/finders/starred_projects_finder.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class StarredProjectsFinder < ProjectsFinder + def initialize(user, params: {}, current_user: nil) + super( + params: params, + current_user: current_user, + project_ids_relation: user.starred_projects.select(:id) + ) + end +end diff --git a/app/finders/users_star_projects_finder.rb b/app/finders/users_star_projects_finder.rb new file mode 100644 index 00000000000..49c4e087b4b --- /dev/null +++ b/app/finders/users_star_projects_finder.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class UsersStarProjectsFinder + include CustomAttributesFilter + + attr_accessor :params + + def initialize(project, params = {}, current_user: nil) + @params = params + @project = project + @current_user = current_user + end + + def execute + stars = UsersStarProject.all + stars = by_project(stars) + stars = by_search(stars) + stars = filter_visible_profiles(stars) + + stars + end + + private + + def by_search(items) + params[:search].present? ? items.search(params[:search]) : items + end + + def by_project(items) + items.by_project(@project) + end + + def filter_visible_profiles(items) + items.with_visible_profile(@current_user) + end +end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 8d0079a4dd3..71c9c121e48 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -601,6 +601,11 @@ module ProjectsHelper end end + def filter_starrer_path(options = {}) + options = params.slice(:sort).merge(options).permit! + "#{request.path}?#{options.to_param}" + end + def sidebar_projects_paths %w[ projects#show diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 15f35645c78..a4eb76a2359 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -167,6 +167,15 @@ module SortingHelper } end + def starrers_sort_options_hash + { + sort_value_name => sort_title_name, + sort_value_name_desc => sort_title_name_desc, + sort_value_recently_created => sort_title_recently_starred, + sort_value_oldest_created => sort_title_oldest_starred + } + end + def sortable_item(item, path, sorted_by) link_to item, path, class: sorted_by == item ? 'is-active' : '' end @@ -327,6 +336,10 @@ module SortingHelper s_('SortOptions|Oldest sign in') end + def sort_title_oldest_starred + s_('SortOptions|Oldest starred') + end + def sort_title_oldest_updated s_('SortOptions|Oldest updated') end @@ -347,6 +360,10 @@ module SortingHelper s_('SortOptions|Recent sign in') end + def sort_title_recently_starred + s_('SortOptions|Recently starred') + end + def sort_title_recently_updated s_('SortOptions|Last updated') end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 73ca17c6605..e38e3378e07 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -89,7 +89,7 @@ module UsersHelper tabs = [] if can?(current_user, :read_user_profile, @user) - tabs += [:overview, :activity, :groups, :contributed, :projects, :snippets] + tabs += [:overview, :activity, :groups, :contributed, :projects, :starred, :snippets] end tabs diff --git a/app/models/user.rb b/app/models/user.rb index 4630552e02e..ac83c8e3256 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -282,6 +282,17 @@ class User < ApplicationRecord scope :for_todos, -> (todos) { where(id: todos.select(:user_id)) } scope :with_emails, -> { preload(:emails) } scope :with_dashboard, -> (dashboard) { where(dashboard: dashboard) } + scope :with_public_profile, -> { where(private_profile: false) } + + def self.with_visible_profile(user) + return with_public_profile if user.nil? + + if user.admin? + all + else + with_public_profile.or(where(id: user.id)) + end + end # Limits the users to those that have TODOs, optionally in the given state. # diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb index 9be6bd2e6f3..3c7a805cc5c 100644 --- a/app/models/users_star_project.rb +++ b/app/models/users_star_project.rb @@ -1,10 +1,37 @@ # frozen_string_literal: true class UsersStarProject < ApplicationRecord + include Sortable + belongs_to :project, counter_cache: :star_count, touch: true belongs_to :user validates :user, presence: true validates :user_id, uniqueness: { scope: [:project_id] } validates :project, presence: true + + alias_attribute :starred_since, :created_at + + scope :order_user_name_asc, -> { joins(:user).merge(User.order_name_asc) } + scope :order_user_name_desc, -> { joins(:user).merge(User.order_name_desc) } + scope :by_project, -> (project) { where(project_id: project.id) } + scope :with_visible_profile, -> (user) { joins(:user).merge(User.with_visible_profile(user)) } + scope :with_public_profile, -> { joins(:user).merge(User.with_public_profile) } + + class << self + def sort_by_attribute(method) + order_method = method || 'id_desc' + + case order_method.to_s + when 'name_asc' then order_user_name_asc + when 'name_desc' then order_user_name_desc + else + order_by(order_method) + end + end + + def search(query) + joins(:user).merge(User.search(query)) + end + end end diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index 021c0b6c429..2b8c9f65d43 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -25,11 +25,11 @@ Members with access to %strong= @group.name %span.badge.badge-pill= @members.total_count - = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form flex-project-members-form' do + = form_tag group_group_members_path(@group), method: :get, class: 'form-inline user-search-form flex-users-form' do .form-group .position-relative.append-right-8 = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } - %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } + %button.user-search-btn{ type: "submit", "aria-label" => "Submit search" } = icon("search") - if can_manage_members = render 'shared/members/filter_2fa_dropdown' diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 090d1549aa7..02e5297528b 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -8,7 +8,8 @@ = sprite_icon('star-o', { css_class: 'icon' }) %span= s_('ProjectOverview|Star') %span.star-count.count-badge-count.d-flex.align-items-center - = @project.star_count + = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'count' do + = @project.star_count - else .count-badge.d-inline-flex.align-item-stretch.append-right-8 @@ -16,4 +17,5 @@ = sprite_icon('star-o', { css_class: 'icon' }) %span= s_('ProjectOverview|Star') %span.star-count.count-badge-count.d-flex.align-items-center - = @project.star_count + = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'count' do + = @project.star_count diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index f220299ec30..5310c1fad01 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -6,11 +6,11 @@ %span.flex-project-title = _("Members of <strong>%{project_name}</strong>").html_safe % { project_name: sanitize(project.name, tags: []) } %span.badge.badge-pill= members.total_count - = form_tag project_project_members_path(project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do + = form_tag project_project_members_path(project), method: :get, class: 'form-inline user-search-form flex-users-form' do .form-group .position-relative = search_field_tag :search, params[:search], { placeholder: _('Find existing members by name'), class: 'form-control', spellcheck: false } - %button.member-search-btn{ type: "submit", "aria-label" => _("Submit search") } + %button.user-search-btn{ type: "submit", "aria-label" => _("Submit search") } = icon("search") = render 'shared/members/sort_dropdown' %ul.content-list.members-list.qa-members-list diff --git a/app/views/projects/starrers/_starrer.html.haml b/app/views/projects/starrers/_starrer.html.haml new file mode 100644 index 00000000000..377d62f8abd --- /dev/null +++ b/app/views/projects/starrers/_starrer.html.haml @@ -0,0 +1,19 @@ +- starrer = local_assigns.fetch(:starrer) + +.col-lg-3.col-md-4.col-sm-12 + .card + .card-body + = image_tag avatar_icon_for_user(starrer.user, 40), class: "avatar s40", alt: '' + + .user-info + .block-truncated + = link_to starrer.user.name, user_path(starrer.user), class: 'user js-user-link', data: { user_id: starrer.user.id } + + .block-truncated + %span.cgray= starrer.user.to_reference + + - if starrer.user == current_user + %span.badge.badge-success.prepend-left-5= _("It's you") + + .block-truncated + = time_ago_with_tooltip(starrer.starred_since) diff --git a/app/views/projects/starrers/index.html.haml b/app/views/projects/starrers/index.html.haml new file mode 100644 index 00000000000..e55ed99f643 --- /dev/null +++ b/app/views/projects/starrers/index.html.haml @@ -0,0 +1,32 @@ +- page_title _("Starrers") + +.top-area.adjust + .nav-text + - full_count_title = "#{@public_count} public and #{@private_count} private" + #{pluralize(@total_count, 'starrer')}: #{full_count_title} + - if @starrers.size > 0 || params[:search].present? + .nav-controls + = form_tag request.original_url, method: :get, class: 'form-inline user-search-form flex-users-form' do + .form-group + .position-relative + = search_field_tag :search, params[:search], { placeholder: _('Search'), class: 'form-control', spellcheck: false } + %button.user-search-btn{ type: "submit", "aria-label" => _("Submit search") } + = icon("search") + .dropdown.inline.user-sort-dropdown + = dropdown_toggle(starrers_sort_options_hash[@sort], { toggle: 'dropdown' }) + %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable + %li.dropdown-header + = _("Sort by") + - starrers_sort_options_hash.each do |value, title| + %li + = link_to filter_starrer_path(sort: value), class: ("is-active" if @sort == value) do + = title +- if @starrers.size > 0 + .row.prepend-top-10 + = render partial: 'starrer', collection: @starrers, as: :starrer + = paginate @starrers, theme: 'gitlab' +- else + - if params[:search].present? + .nothing-here-block= _('No starrers matched your search') + - else + .nothing-here-block= _('Nobody has starred this repository yet') diff --git a/app/views/shared/members/_sort_dropdown.html.haml b/app/views/shared/members/_sort_dropdown.html.haml index 59bdfb73e6e..f5ebab035db 100644 --- a/app/views/shared/members/_sort_dropdown.html.haml +++ b/app/views/shared/members/_sort_dropdown.html.haml @@ -1,4 +1,4 @@ -.dropdown.inline.member-sort-dropdown +.dropdown.inline.user-sort-dropdown = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' }) %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable %li.dropdown-header diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 67cb1aa549c..bb05658c719 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -17,15 +17,20 @@ - contributed_projects_illustration_path = 'illustrations/profile-page/contributed-projects.svg' - contributed_projects_current_user_empty_message_header = s_('UserProfile|Explore public groups to find projects to contribute to.') - contributed_projects_visitor_empty_message = s_('UserProfile|This user hasn\'t contributed to any projects') +- starred_projects_illustration_path = 'illustrations/starred_empty.svg' +- starred_projects_current_user_empty_message_header = s_('UserProfile|Star projects to track their progress and show your appreciation.') +- starred_projects_visitor_empty_message = s_('UserProfile|This user hasn\'t starred any projects') - own_projects_illustration_path = 'illustrations/profile-page/personal-project.svg' - own_projects_current_user_empty_message_header = s_('UserProfile|You haven\'t created any personal projects.') - own_projects_current_user_empty_message_description = s_('UserProfile|Your projects can be available publicly, internally, or privately, at your choice.') - own_projects_visitor_empty_message = s_('UserProfile|This user doesn\'t have any personal projects') - explore_page_empty_message = s_('UserProfile|Explore public groups to find projects to contribute to.') -- primary_button_label = _('New project') -- primary_button_link = new_project_path -- secondary_button_label = _('Explore groups') -- secondary_button_link = explore_groups_path +- new_project_button_label = _('New project') +- new_project_button_link = new_project_path +- explore_projects_button_label = _('Explore projects') +- explore_projects_button_link = explore_projects_path +- explore_groups_button_label = _('Explore groups') +- explore_groups_button_link = explore_groups_path .js-projects-list-holder - if any_projects?(projects) @@ -48,15 +53,21 @@ - if @contributed_projects = render partial: 'shared/empty_states/profile_tabs', locals: { illustration_path: contributed_projects_illustration_path, current_user_empty_message_header: contributed_projects_current_user_empty_message_header, - primary_button_label: primary_button_label, - primary_button_link: primary_button_link, - secondary_button_label: secondary_button_label, - secondary_button_link: secondary_button_link, + primary_button_label: new_project_button_label, + primary_button_link: new_project_button_link, + secondary_button_label: explore_groups_button_label, + secondary_button_link: explore_groups_button_link, visitor_empty_message: contributed_projects_visitor_empty_message } + - elsif @starred_projects + = render partial: 'shared/empty_states/profile_tabs', locals: { illustration_path: starred_projects_illustration_path, + current_user_empty_message_header: starred_projects_current_user_empty_message_header, + primary_button_label: explore_projects_button_label, + primary_button_link: explore_projects_button_link, + visitor_empty_message: starred_projects_visitor_empty_message } - else = render partial: 'shared/empty_states/profile_tabs', locals: { illustration_path: own_projects_illustration_path, current_user_empty_message_header: own_projects_current_user_empty_message_header, current_user_empty_message_description: own_projects_current_user_empty_message_description, - primary_button_label: primary_button_label, - primary_button_link: primary_button_link, + primary_button_label: new_project_button_label, + primary_button_link: new_project_button_link, visitor_empty_message: defined?(explore_page) && explore_page ? explore_page_empty_message : own_projects_visitor_empty_message } diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index f40a9cffb29..b7474d891dc 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -63,7 +63,9 @@ - if project.archived %span.d-flex.icon-wrapper.badge.badge-warning archived - if stars - %span.d-flex.align-items-center.icon-wrapper.stars.has-tooltip{ data: { container: 'body', placement: 'top' }, title: _('Stars') } + = link_to project_starrers_path(project), + class: "d-flex align-items-center icon-wrapper stars has-tooltip", + title: _('Stars'), data: { container: 'body', placement: 'top' } do = sprite_icon('star', size: 14, css_class: 'append-right-4') = number_with_delimiter(project.star_count) - if forks diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index b3a73030859..73bee7c2586 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -111,6 +111,10 @@ %li.js-projects-tab = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do = s_('UserProfile|Personal projects') + - if profile_tab?(:starred) + %li.js-starred-tab + = link_to user_starred_projects_path, data: { target: 'div#starred', action: 'starred', toggle: 'tab', endpoint: user_starred_projects_path(format: :json) } do + = s_('UserProfile|Starred projects') - if profile_tab?(:snippets) %li.js-snippets-tab = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do @@ -142,6 +146,10 @@ #projects.tab-pane -# This tab is always loaded via AJAX + - if profile_tab?(:starred) + #starred.tab-pane + -# This tab is always loaded via AJAX + - if profile_tab?(:snippets) #snippets.tab-pane -# This tab is always loaded via AJAX |