Commit 27ec84e3 authored by Annabel Dunstone Gray's avatar Annabel Dunstone Gray

Merge branch '49403-redesign-activity-feed' into 'master'

Resolve "Redesign activity feed"

Closes #49403

See merge request gitlab-org/gitlab-ce!22217
parents c8b6b9f2 31532b2d
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
svg { svg {
fill: currentColor; fill: currentColor;
$svg-sizes: 8 10 12 16 18 24 32 48 72; $svg-sizes: 8 10 12 14 16 18 24 32 48 72;
@each $svg-size in $svg-sizes { @each $svg-size in $svg-sizes {
&.s#{$svg-size} { &.s#{$svg-size} {
@include svg-size(#{$svg-size}px); @include svg-size(#{$svg-size}px);
......
...@@ -269,6 +269,7 @@ $flash-height: 52px; ...@@ -269,6 +269,7 @@ $flash-height: 52px;
$context-header-height: 60px; $context-header-height: 60px;
$breadcrumb-min-height: 48px; $breadcrumb-min-height: 48px;
$project-title-row-height: 24px; $project-title-row-height: 24px;
$gl-line-height: 16px;
/* /*
* Common component specific colors * Common component specific colors
......
...@@ -4,41 +4,29 @@ ...@@ -4,41 +4,29 @@
*/ */
.event-item { .event-item {
font-size: $gl-font-size; font-size: $gl-font-size;
padding: $gl-padding-top 0 $gl-padding-top 40px; padding: $gl-padding 0 $gl-padding 56px;
border-bottom: 1px solid $white-normal; border-bottom: 1px solid $white-normal;
color: $gl-text-color; color: $gl-text-color-secondary;
position: relative; position: relative;
line-height: $gl-line-height;
&.event-inline {
.system-note-image { .system-note-image {
top: 20px; position: absolute;
} left: 0;
.user-avatar {
top: 14px;
}
.event-title, svg {
.event-item-timestamp { fill: $gl-text-color-secondary;
line-height: 40px;
} }
} }
a { .system-note-image-inline {
color: $gl-text-color;
}
.system-note-image {
position: absolute;
left: 0;
top: 14px;
svg { svg {
width: 20px;
height: 20px;
fill: $gl-text-color-secondary; fill: $gl-text-color-secondary;
} }
}
.system-note-image,
.system-note-image-inline {
&.opened-icon, &.opened-icon,
&.created-icon { &.created-icon {
svg { svg {
...@@ -53,16 +41,35 @@ ...@@ -53,16 +41,35 @@
&.accepted-icon svg { &.accepted-icon svg {
fill: $blue-300; fill: $blue-300;
} }
&.commented-on-icon svg {
fill: $blue-600;
}
} }
.event-title { .event-user-info {
@include str-truncated(calc(100% - 174px)); margin-bottom: $gl-padding-8;
font-weight: $gl-font-weight-bold;
.author_name {
a {
color: $gl-text-color; color: $gl-text-color;
font-weight: $gl-font-weight-bold;
}
}
}
.event-title {
.event-type {
&::first-letter {
text-transform: capitalize;
}
}
} }
.event-body { .event-body {
margin-top: $gl-padding-8;
margin-right: 174px; margin-right: 174px;
color: $gl-text-color;
.event-note { .event-note {
word-wrap: break-word; word-wrap: break-word;
...@@ -92,7 +99,7 @@ ...@@ -92,7 +99,7 @@
} }
.note-image-attach { .note-image-attach {
margin-top: 4px; margin-top: $gl-padding-4;
margin-left: 0; margin-left: 0;
max-width: 200px; max-width: 200px;
float: none; float: none;
...@@ -107,7 +114,6 @@ ...@@ -107,7 +114,6 @@
color: $gl-gray-500; color: $gl-gray-500;
float: left; float: left;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 16px;
margin-right: 5px; margin-right: 5px;
} }
} }
...@@ -127,7 +133,9 @@ ...@@ -127,7 +133,9 @@
} }
} }
&:last-child { border: 0; } &:last-child {
border: 0;
}
.event_commits { .event_commits {
li { li {
...@@ -154,7 +162,6 @@ ...@@ -154,7 +162,6 @@
.event-item-timestamp { .event-item-timestamp {
float: right; float: right;
line-height: 22px;
} }
} }
...@@ -177,10 +184,8 @@ ...@@ -177,10 +184,8 @@
.event-item { .event-item {
padding-left: 0; padding-left: 0;
&.event-inline { .event-user-info {
.event-title { margin-bottom: $gl-padding-4;
line-height: 20px;
}
} }
.event-title { .event-title {
...@@ -194,7 +199,8 @@ ...@@ -194,7 +199,8 @@
} }
.event-body { .event-body {
margin: 0; margin-top: $gl-padding-4;
margin-right: 0;
padding-left: 0; padding-left: 0;
} }
......
...@@ -240,6 +240,12 @@ ...@@ -240,6 +240,12 @@
left: 0; left: 0;
} }
.activities-block {
.event-item {
padding-left: 40px;
}
}
@include media-breakpoint-down(xs) { @include media-breakpoint-down(xs) {
.cover-block { .cover-block {
padding-top: 20px; padding-top: 20px;
...@@ -267,6 +273,12 @@ ...@@ -267,6 +273,12 @@
margin-right: 0; margin-right: 0;
} }
} }
.activities-block {
.event-item {
padding-left: 0;
}
}
} }
} }
......
...@@ -163,14 +163,10 @@ module EventsHelper ...@@ -163,14 +163,10 @@ module EventsHelper
def event_note_title_html(event) def event_note_title_html(event)
if event.note_target if event.note_target
text = raw("#{event.note_target_type} ") + capture do
if event.commit_note? concat content_tag(:span, event.note_target_type, class: "event-target-type append-right-4")
content_tag(:span, event.note_target_reference, class: 'commit-sha') concat link_to(event.note_target_reference, event_note_target_url(event), title: event.target_title, class: 'has-tooltip event-target-link append-right-4')
else
event.note_target_reference
end end
link_to(text, event_note_target_url(event), title: event.target_title, class: 'has-tooltip')
else else
content_tag(:strong, '(deleted)') content_tag(:strong, '(deleted)')
end end
...@@ -183,17 +179,9 @@ module EventsHelper ...@@ -183,17 +179,9 @@ module EventsHelper
"--broken encoding" "--broken encoding"
end end
def event_row_class(event) def icon_for_event(note, size: 24)
if event.body?
"event-block"
else
"event-inline"
end
end
def icon_for_event(note)
icon_name = ICON_NAMES_BY_EVENT_TYPE[note] icon_name = ICON_NAMES_BY_EVENT_TYPE[note]
sprite_icon(icon_name) if icon_name sprite_icon(icon_name, size: size) if icon_name
end end
def icon_for_profile_event(event) def icon_for_profile_event(event)
...@@ -203,8 +191,24 @@ module EventsHelper ...@@ -203,8 +191,24 @@ module EventsHelper
end end
else else
content_tag :div, class: 'system-note-image user-avatar' do content_tag :div, class: 'system-note-image user-avatar' do
author_avatar(event, size: 32) author_avatar(event, size: 40)
end end
end end
end end
def inline_event_icon(event)
unless current_path?('users#show')
content_tag :span, class: "system-note-image-inline d-none d-sm-flex append-right-4 #{event.action_name.parameterize}-icon align-self-center" do
icon_for_event(event.action_name, size: 14)
end
end
end
def event_user_info(event)
content_tag(:div, class: "event-user-info") do
concat content_tag(:span, link_to_author(event), class: "author_name")
concat " ".html_safe
concat content_tag(:span, event.author.to_reference, class: "username")
end
end
end end
- if event.visible_to_user?(current_user) - if event.visible_to_user?(current_user)
.event-item{ class: event_row_class(event) } .event-item
.event-item-timestamp .event-item-timestamp
#{time_ago_with_tooltip(event.created_at)} #{time_ago_with_tooltip(event.created_at)}
......
= icon_for_profile_event(event) = icon_for_profile_event(event)
.event-title = event_user_info(event)
%span.author_name= link_to_author(event)
%span{ class: event.action_name } .event-title.d-flex.flex-wrap
= inline_event_icon(event)
- if event.target - if event.target
%span.event-type.d-inline-block.append-right-4{ class: event.action_name }
= event.action_name = event.action_name
%strong %span.event-target-type.append-right-4= event.target_type.titleize.downcase
= link_to [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title do = link_to [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip event-target-link append-right-4', title: event.target_title do
= event.target_type.titleize.downcase
= event.target.reference_link_text = event.target.reference_link_text
- unless event.milestone?
%span.event-target-title.append-right-4= """.html_safe + event.target.title + "&quot".html_safe
- else - else
%span.event-type.d-inline-block.append-right-4{ class: event.action_name }
= event_action_name(event) = event_action_name(event)
= render "events/event_scope", event: event = render "events/event_scope", event: event
- if event.target.respond_to?(:title)
.event-body
.event-note
= event.target.title
= icon_for_profile_event(event) = icon_for_profile_event(event)
.event-title = event_user_info(event)
%span.author_name= link_to_author(event)
%span{ class: event.action_name } .event-title.d-flex.flex-wrap
= inline_event_icon(event)
%span.event-type.d-inline-block.append-right-4{ class: event.action_name }
= event_action_name(event) = event_action_name(event)
- if event.project - if event.project
......
= icon_for_profile_event(event) = icon_for_profile_event(event)
.event-title = event_user_info(event)
%span.author_name= link_to_author(event)
.event-title.d-flex.flex-wrap
= inline_event_icon(event)
%span.event-type.d-inline-block.append-right-4{ class: event.action_name }
= event.action_name = event.action_name
= event_note_title_html(event) = event_note_title_html(event)
%span.event-target-title.append-right-4= """.html_safe + event.target.title + "&quot".html_safe
= render "events/event_scope", event: event = render "events/event_scope", event: event
......
.event-inline.event-item .event-item
.event-item-timestamp .event-item-timestamp
= time_ago_with_tooltip(event.created_at) = time_ago_with_tooltip(event.created_at)
.system-note-image= sprite_icon('eye-slash', size: 16, css_class: 'icon') .system-note-image= sprite_icon('eye-slash', size: 24, css_class: 'icon')
.event-title = event_user_info(event)
- author_name = capture do
%span.author_name= link_to_author(event) .event-title.d-flex.flex-wrap
= s_('Profiles|%{author_name} made a private contribution').html_safe % { author_name: author_name } = inline_event_icon(event)
= s_('Profiles|Made a private contribution')
...@@ -2,13 +2,15 @@ ...@@ -2,13 +2,15 @@
= icon_for_profile_event(event) = icon_for_profile_event(event)
.event-title = event_user_info(event)
%span.author_name= link_to_author(event)
%span.pushed #{event.action_name} #{event.ref_type} .event-title.d-flex.flex-wrap
%strong = inline_event_icon(event)
%span.event-type.d-inline-block.append-right-4.pushed #{event.action_name} #{event.ref_type}
%span
- commits_link = project_commits_path(project, event.ref_name) - commits_link = project_commits_path(project, event.ref_name)
- should_link = event.tag? ? project.repository.tag_exists?(event.ref_name) : project.repository.branch_exists?(event.ref_name) - should_link = event.tag? ? project.repository.tag_exists?(event.ref_name) : project.repository.branch_exists?(event.ref_name)
= link_to_if should_link, event.ref_name, commits_link, class: 'ref-name' = link_to_if should_link, event.ref_name, commits_link, class: 'ref-name append-right-4'
= render "events/event_scope", event: event = render "events/event_scope", event: event
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
- if can?(current_user, :read_cross_project) - if can?(current_user, :read_cross_project)
.activities-block .activities-block
.content-block .border-bottom.prepend-top-16
%h5.prepend-top-10 %h5
= s_('UserProfile|Recent contributions') = s_('UserProfile|Recent contributions')
.overview-content-list{ data: { href: user_path } } .overview-content-list{ data: { href: user_path } }
.center.light.loading .center.light.loading
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
.col-md-12.col-lg-6 .col-md-12.col-lg-6
.projects-block .projects-block
.content-block .border-bottom.prepend-top-16
%h4 %h4
= s_('UserProfile|Personal projects') = s_('UserProfile|Personal projects')
.overview-content-list{ data: { href: user_projects_path } } .overview-content-list{ data: { href: user_projects_path } }
......
title: Redesign activity feed
merge_request: 22217
author:
type: other
...@@ -4641,9 +4641,6 @@ msgstr "" ...@@ -4641,9 +4641,6 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible." msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "" msgstr ""
msgid "Profiles|%{author_name} made a private contribution"
msgstr ""
msgid "Profiles|Account scheduled for removal." msgid "Profiles|Account scheduled for removal."
msgstr "" msgstr ""
...@@ -4704,6 +4701,9 @@ msgstr "" ...@@ -4704,6 +4701,9 @@ msgstr ""
msgid "Profiles|Invalid username" msgid "Profiles|Invalid username"
msgstr "" msgstr ""
msgid "Profiles|Made a private contribution"
msgstr ""
msgid "Profiles|Main settings" msgid "Profiles|Main settings"
msgstr "" msgstr ""
......
...@@ -153,7 +153,7 @@ describe 'Contributions Calendar', :js do ...@@ -153,7 +153,7 @@ describe 'Contributions Calendar', :js do
include_context 'visit user page' include_context 'visit user page'
it 'displays calendar activity log' do it 'displays calendar activity log' do
expect(find('.tab-pane#activity .content_list .event-note')).to have_content issue_title expect(find('.tab-pane#activity .content_list .event-target-title')).to have_content issue_title
end end
end end
end end
......
...@@ -14,14 +14,15 @@ describe 'Project member activity', :js do ...@@ -14,14 +14,15 @@ describe 'Project member activity', :js do
wait_for_requests wait_for_requests
end end
subject { page.find(".event-title").text }
context 'when a user joins the project' do context 'when a user joins the project' do
before do before do
visit_activities_and_wait_with_event(Event::JOINED) visit_activities_and_wait_with_event(Event::JOINED)
end end
it { is_expected.to eq("#{user.name} joined project") } it "presents the correct message" do
expect(page.find('.event-user-info').text).to eq("#{user.name} #{user.to_reference}")
expect(page.find('.event-title').text).to eq("joined project")
end
end end
context 'when a user leaves the project' do context 'when a user leaves the project' do
...@@ -29,7 +30,10 @@ describe 'Project member activity', :js do ...@@ -29,7 +30,10 @@ describe 'Project member activity', :js do
visit_activities_and_wait_with_event(Event::LEFT) visit_activities_and_wait_with_event(Event::LEFT)
end end
it { is_expected.to eq("#{user.name} left project") } it "presents the correct message" do
expect(page.find('.event-user-info').text).to eq("#{user.name} #{user.to_reference}")
expect(page.find('.event-title').text).to eq("left project")
end
end end
context 'when a users membership expires for the project' do context 'when a users membership expires for the project' do
...@@ -38,8 +42,8 @@ describe 'Project member activity', :js do ...@@ -38,8 +42,8 @@ describe 'Project member activity', :js do
end end
it "presents the correct message" do it "presents the correct message" do
message = "#{user.name} removed due to membership expiration from project" expect(page.find('.event-user-info').text).to eq("#{user.name} #{user.to_reference}")
is_expected.to eq(message) expect(page.find('.event-title').text).to eq("removed due to membership expiration from project")
end end
end end
end end
...@@ -24,6 +24,6 @@ describe "User creates milestone", :js do ...@@ -24,6 +24,6 @@ describe "User creates milestone", :js do
visit(activity_project_path(project)) visit(activity_project_path(project))
expect(page).to have_content("#{user.name} opened milestone") expect(page).to have_content("#{user.name} #{user.to_reference} opened milestone")
end end
end end
...@@ -23,7 +23,7 @@ describe "User deletes milestone", :js do ...@@ -23,7 +23,7 @@ describe "User deletes milestone", :js do
visit(activity_project_path(project)) visit(activity_project_path(project))
expect(page).to have_content("#{user.name} destroyed milestone") expect(page).to have_content("#{user.name} #{user.to_reference} destroyed milestone")
end end
end end
......
...@@ -19,13 +19,13 @@ describe 'Projects > Activity > User sees activity' do ...@@ -19,13 +19,13 @@ describe 'Projects > Activity > User sees activity' do
it 'shows the last push in the activity page', :js do it 'shows the last push in the activity page', :js do
visit activity_project_path(project) visit activity_project_path(project)
expect(page).to have_content "#{user.name} pushed new branch fix" expect(page).to have_content "#{user.name} #{user.to_reference} pushed new branch fix"
end end
it 'allows to filter event with the "event_filter=issue" URL param', :js do it 'allows to filter event with the "event_filter=issue" URL param', :js do
visit activity_project_path(project, event_filter: 'issue') visit activity_project_path(project, event_filter: 'issue')
expect(page).not_to have_content "#{user.name} pushed new branch fix" expect(page).not_to have_content "#{user.name} #{user.to_reference} pushed new branch fix"
expect(page).to have_content "#{user.name} opened issue #{issue.to_reference}" expect(page).to have_content "#{user.name} #{user.to_reference} opened issue #{issue.to_reference}"
end end
end end
...@@ -5,7 +5,7 @@ describe 'Project > Activity > User sees private activity', :js do ...@@ -5,7 +5,7 @@ describe 'Project > Activity > User sees private activity', :js do
let(:author) { create(:user) } let(:author) { create(:user) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:issue) { create(:issue, :confidential, project: project, author: author) } let(:issue) { create(:issue, :confidential, project: project, author: author) }
let(:message) { "#{author.name} opened issue #{issue.to_reference}" } let(:message) { "#{author.name} #{author.to_reference} opened issue #{issue.to_reference}" }
before do before do
project.add_developer(author) project.add_developer(author)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment