Commit 7e17f9bc authored by Tim Zallmann's avatar Tim Zallmann

Merge branch '35224-transform-user-profile-javascript-into-async-bundle' into 'master'

Resolve "Transform user profile javascript into async bundle"

Closes #35224

See merge request !12929
parents b5aac468 288e8ea1
...@@ -538,6 +538,13 @@ import GpgBadges from './gpg_badges'; ...@@ -538,6 +538,13 @@ import GpgBadges from './gpg_badges';
case 'protected_branches': case 'protected_branches':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
} }
break;
case 'users':
const action = path[1];
import(/* webpackChunkName: 'user_profile' */ './users')
.then(user => user.default(action))
.catch(() => {});
break;
} }
// If we haven't installed a custom shortcut handler, install the default one // If we haven't installed a custom shortcut handler, install the default one
if (!shortcut_handler) { if (!shortcut_handler) {
......
import ActivityCalendar from './activity_calendar'; import Cookies from 'js-cookie';
import User from './user'; import UserTabs from './user_tabs';
// use legacy exports until embedded javascript is refactored export default function initUserProfile(action) {
window.Calendar = ActivityCalendar; // place profile avatars to top
window.gl = window.gl || {}; $('.profile-groups-avatars').tooltip({
window.gl.User = User; placement: 'top',
});
// eslint-disable-next-line no-new
new UserTabs({ parentEl: '.user-profile', action });
// hide project limit message
$('.hide-project-limit-message').on('click', (e) => {
e.preventDefault();
Cookies.set('hide_project_limit_message', 'false');
$(this).parents('.project-limit-message').remove();
});
}
/* eslint-disable class-methods-use-this */
import Cookies from 'js-cookie';
import UserTabs from './user_tabs';
export default class User {
constructor({ action }) {
this.action = action;
this.placeProfileAvatarsToTop();
this.initTabs();
this.hideProjectLimitMessage();
}
placeProfileAvatarsToTop() {
$('.profile-groups-avatars').tooltip({
placement: 'top',
});
}
initTabs() {
return new UserTabs({
parentEl: '.user-profile',
action: this.action,
});
}
hideProjectLimitMessage() {
$('.hide-project-limit-message').on('click', (e) => {
e.preventDefault();
Cookies.set('hide_project_limit_message', 'false');
$(this).parents('.project-limit-message').remove();
});
}
}
/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign, class-methods-use-this */ import ActivityCalendar from './activity_calendar';
/* /**
UserTabs * UserTabs
*
Handles persisting and restoring the current tab selection and lazily-loading * Handles persisting and restoring the current tab selection and lazily-loading
content on the Users#show page. * content on the Users#show page.
*
### Example Markup * ### Example Markup
*
<ul class="nav-links"> * <ul class="nav-links">
<li class="activity-tab active"> * <li class="activity-tab active">
<a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username"> * <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
Activity * Activity
</a> * </a>
</li> * </li>
<li class="groups-tab"> * <li class="groups-tab">
<a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups"> * <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
Groups * Groups
</a> * </a>
</li> * </li>
<li class="contributed-tab"> * <li class="contributed-tab">
<a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed"> * ...
Contributed projects * </li>
</a> * <li class="projects-tab">
</li> * ...
<li class="projects-tab"> * </li>
<a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects"> * <li class="snippets-tab">
Personal projects * ...
</a> * </li>
</li> * </ul>
<li class="snippets-tab"> *
<a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets"> * <div class="tab-content">
</a> * <div class="tab-pane" id="activity">
</li> * Activity Content
</ul> * </div>
* <div class="tab-pane" id="groups">
<div class="tab-content"> * Groups Content
<div class="tab-pane" id="activity"> * </div>
Activity Content * <div class="tab-pane" id="contributed">
</div> * Contributed projects content
<div class="tab-pane" id="groups"> * </div>
Groups Content * <div class="tab-pane" id="projects">
</div> * Projects content
<div class="tab-pane" id="contributed"> * </div>
Contributed projects content * <div class="tab-pane" id="snippets">
</div> * Snippets content
<div class="tab-pane" id="projects"> * </div>
Projects content * </div>
</div> *
<div class="tab-pane" id="snippets"> * <div class="loading-status">
Snippets content * <div class="loading">
* Loading Animation
* </div>
* </div>
*/
const CALENDAR_TEMPLATE = `
<div class="clearfix calendar">
<div class="js-contrib-calendar"></div>
<div class="calendar-hint">
Summary of issues, merge requests, push events, and comments
</div> </div>
</div> </div>
`;
<div class="loading-status">
<div class="loading">
Loading Animation
</div>
</div>
*/
export default class UserTabs { export default class UserTabs {
constructor ({ defaultAction, action, parentEl }) { constructor({ defaultAction, action, parentEl }) {
this.loaded = {}; this.loaded = {};
this.defaultAction = defaultAction || 'activity'; this.defaultAction = defaultAction || 'activity';
this.action = action || this.defaultAction; this.action = action || this.defaultAction;
this.$parentEl = $(parentEl) || $(document); this.$parentEl = $(parentEl) || $(document);
this._location = window.location; this.windowLocation = window.location;
this.$parentEl.find('.nav-links a') this.$parentEl.find('.nav-links a')
.each((i, navLink) => { .each((i, navLink) => {
this.loaded[$(navLink).attr('data-action')] = false; this.loaded[$(navLink).attr('data-action')] = false;
...@@ -82,12 +86,10 @@ export default class UserTabs { ...@@ -82,12 +86,10 @@ export default class UserTabs {
} }
bindEvents() { bindEvents() {
this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this); this.$parentEl
.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event))
.on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)); .on('click', '.gl-pagination a', event => this.changeProjectsPage(event));
this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper);
} }
changeProjectsPage(e) { changeProjectsPage(e) {
...@@ -122,7 +124,7 @@ export default class UserTabs { ...@@ -122,7 +124,7 @@ export default class UserTabs {
const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; const loadableActions = ['groups', 'contributed', 'projects', 'snippets'];
if (loadableActions.indexOf(action) > -1) { if (loadableActions.indexOf(action) > -1) {
return this.loadTab(action, endpoint); this.loadTab(action, endpoint);
} }
} }
...@@ -131,25 +133,38 @@ export default class UserTabs { ...@@ -131,25 +133,38 @@ export default class UserTabs {
beforeSend: () => this.toggleLoading(true), beforeSend: () => this.toggleLoading(true),
complete: () => this.toggleLoading(false), complete: () => this.toggleLoading(false),
dataType: 'json', dataType: 'json',
type: 'GET',
url: endpoint, url: endpoint,
success: (data) => { success: (data) => {
const tabSelector = `div#${action}`; const tabSelector = `div#${action}`;
this.$parentEl.find(tabSelector).html(data.html); this.$parentEl.find(tabSelector).html(data.html);
this.loaded[action] = true; this.loaded[action] = true;
return gl.utils.localTimeAgo($('.js-timeago', tabSelector)); gl.utils.localTimeAgo($('.js-timeago', tabSelector));
} },
}); });
} }
loadActivities() { loadActivities() {
if (this.loaded['activity']) { if (this.loaded.activity) {
return; return;
} }
const $calendarWrap = this.$parentEl.find('.user-calendar'); const $calendarWrap = this.$parentEl.find('.user-calendar');
$calendarWrap.load($calendarWrap.data('href')); const calendarPath = $calendarWrap.data('calendarPath');
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
$.ajax({
dataType: 'json',
url: calendarPath,
success: (activityData) => {
$calendarWrap.html(CALENDAR_TEMPLATE);
// eslint-disable-next-line no-new
new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath);
},
});
// eslint-disable-next-line no-new
new gl.Activities(); new gl.Activities();
return this.loaded['activity'] = true; this.loaded.activity = true;
} }
toggleLoading(status) { toggleLoading(status) {
...@@ -158,13 +173,13 @@ export default class UserTabs { ...@@ -158,13 +173,13 @@ export default class UserTabs {
} }
setCurrentAction(source) { setCurrentAction(source) {
let new_state = source; let newState = source;
new_state = new_state.replace(/\/+$/, ''); newState = newState.replace(/\/+$/, '');
new_state += this._location.search + this._location.hash; newState += this.windowLocation.search + this.windowLocation.hash;
history.replaceState({ history.replaceState({
url: new_state url: newState,
}, document.title, new_state); }, document.title, newState);
return new_state; return newState;
} }
getCurrentAction() { getCurrentAction() {
......
...@@ -73,10 +73,7 @@ class UsersController < ApplicationController ...@@ -73,10 +73,7 @@ class UsersController < ApplicationController
end end
def calendar def calendar
calendar = contributions_calendar render json: contributions_calendar.activity_dates
@activity_dates = calendar.activity_dates
render 'calendar', layout: false
end end
def calendar_activities def calendar_activities
......
.clearfix.calendar
.js-contrib-calendar
.calendar-hint
Summary of issues, merge requests, push events, and comments
:javascript
new Calendar(
#{@activity_dates.to_json},
'#{user_calendar_activities_path}'
);
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
- @hide_breadcrumbs = true - @hide_breadcrumbs = true
- page_title @user.name - page_title @user.name
- page_description @user.bio - page_description @user.bio
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('users')
- header_title @user.name, user_path(@user) - header_title @user.name, user_path(@user)
- @no_container = true - @no_container = true
...@@ -107,7 +104,7 @@ ...@@ -107,7 +104,7 @@
.tab-content .tab-content
#activity.tab-pane #activity.tab-pane
.row-content-block.calender-block.white.second-block.hidden-xs .row-content-block.calender-block.white.second-block.hidden-xs
.user-calendar{ data: { href: user_calendar_path } } .user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path } }
%h4.center.light %h4.center.light
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
.user-calendar-activities .user-calendar-activities
...@@ -131,10 +128,3 @@ ...@@ -131,10 +128,3 @@
.loading-status .loading-status
= spinner = spinner
:javascript
var userProfile;
userProfile = new gl.User({
action: "#{controller.action_name}"
});
...@@ -67,7 +67,6 @@ var config = { ...@@ -67,7 +67,6 @@ var config = {
stl_viewer: './blob/stl_viewer.js', stl_viewer: './blob/stl_viewer.js',
terminal: './terminal/terminal_bundle.js', terminal: './terminal/terminal_bundle.js',
u2f: ['vendor/u2f'], u2f: ['vendor/u2f'],
users: './users/index.js',
raven: './raven/index.js', raven: './raven/index.js',
vue_merge_request_widget: './vue_merge_request_widget/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js',
test: './test.js', test: './test.js',
...@@ -185,7 +184,6 @@ var config = { ...@@ -185,7 +184,6 @@ var config = {
name: 'common_d3', name: 'common_d3',
chunks: [ chunks: [
'graphs', 'graphs',
'users',
'monitoring', 'monitoring',
], ],
}), }),
......
...@@ -80,9 +80,9 @@ describe UsersController do ...@@ -80,9 +80,9 @@ describe UsersController do
it 'renders calendar' do it 'renders calendar' do
sign_in(user) sign_in(user)
get :calendar, username: user.username get :calendar, username: user.username, format: :json
expect(response).to render_template('calendar') expect(response).to have_http_status(200)
end end
context 'forked project' do context 'forked project' do
......
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