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
This commit is contained in:
commit
7e17f9bc89
10 changed files with 241 additions and 268 deletions
|
@ -538,6 +538,13 @@ import GpgBadges from './gpg_badges';
|
|||
case 'protected_branches':
|
||||
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 (!shortcut_handler) {
|
||||
|
|
|
@ -1,10 +1,28 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */
|
||||
|
||||
import d3 from 'd3';
|
||||
|
||||
const LOADING_HTML = `
|
||||
<div class="text-center">
|
||||
<i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i>
|
||||
</div>
|
||||
`;
|
||||
|
||||
function formatTooltipText({ date, count }) {
|
||||
const dateObject = new Date(date);
|
||||
const dateDayName = gl.utils.getDayName(dateObject);
|
||||
const dateText = dateObject.format('mmm d, yyyy');
|
||||
|
||||
let contribText = 'No contributions';
|
||||
if (count > 0) {
|
||||
contribText = `${count} contribution${count > 1 ? 's' : ''}`;
|
||||
}
|
||||
return `${contribText}<br />${dateDayName} ${dateText}`;
|
||||
}
|
||||
|
||||
const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
|
||||
|
||||
export default class ActivityCalendar {
|
||||
constructor(timestamps, calendar_activities_path) {
|
||||
this.calendar_activities_path = calendar_activities_path;
|
||||
constructor(container, timestamps, calendarActivitiesPath) {
|
||||
this.calendarActivitiesPath = calendarActivitiesPath;
|
||||
this.clickDay = this.clickDay.bind(this);
|
||||
this.currentSelectedDate = '';
|
||||
this.daySpace = 1;
|
||||
|
@ -12,25 +30,26 @@ export default class ActivityCalendar {
|
|||
this.daySizeWithSpace = this.daySize + (this.daySpace * 2);
|
||||
this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
this.months = [];
|
||||
|
||||
// Loop through the timestamps to create a group of objects
|
||||
// The group of objects will be grouped based on the day of the week they are
|
||||
this.timestampsTmp = [];
|
||||
var group = 0;
|
||||
let group = 0;
|
||||
|
||||
var today = new Date();
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0, 0);
|
||||
|
||||
var oneYearAgo = new Date(today);
|
||||
const oneYearAgo = new Date(today);
|
||||
oneYearAgo.setFullYear(today.getFullYear() - 1);
|
||||
|
||||
var days = gl.utils.getDayDifference(oneYearAgo, today);
|
||||
const days = gl.utils.getDayDifference(oneYearAgo, today);
|
||||
|
||||
for (var i = 0; i <= days; i += 1) {
|
||||
var date = new Date(oneYearAgo);
|
||||
for (let i = 0; i <= days; i += 1) {
|
||||
const date = new Date(oneYearAgo);
|
||||
date.setDate(date.getDate() + i);
|
||||
|
||||
var day = date.getDay();
|
||||
var count = timestamps[date.format('yyyy-mm-dd')];
|
||||
const day = date.getDay();
|
||||
const count = timestamps[date.format('yyyy-mm-dd')] || 0;
|
||||
|
||||
// Create a new group array if this is the first day of the week
|
||||
// or if is first object
|
||||
|
@ -39,129 +58,119 @@ export default class ActivityCalendar {
|
|||
group += 1;
|
||||
}
|
||||
|
||||
var innerArray = this.timestampsTmp[group - 1];
|
||||
// Push to the inner array the values that will be used to render map
|
||||
innerArray.push({
|
||||
count: count || 0,
|
||||
date: date,
|
||||
day: day
|
||||
});
|
||||
const innerArray = this.timestampsTmp[group - 1];
|
||||
innerArray.push({ count, date, day });
|
||||
}
|
||||
|
||||
// Init color functions
|
||||
this.colorKey = this.initColorKey();
|
||||
this.colorKey = initColorKey();
|
||||
this.color = this.initColor();
|
||||
|
||||
// Init the svg element
|
||||
this.renderSvg(group);
|
||||
this.svg = this.renderSvg(container, group);
|
||||
this.renderDays();
|
||||
this.renderMonths();
|
||||
this.renderDayTitles();
|
||||
this.renderKey();
|
||||
this.initTooltips();
|
||||
|
||||
// Init tooltips
|
||||
$(`${container} .js-tooltip`).tooltip({ html: true });
|
||||
}
|
||||
|
||||
// Add extra padding for the last month label if it is also the last column
|
||||
getExtraWidthPadding(group) {
|
||||
var extraWidthPadding = 0;
|
||||
var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth();
|
||||
var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth();
|
||||
let extraWidthPadding = 0;
|
||||
const lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth();
|
||||
const secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth();
|
||||
|
||||
if (lastColMonth != secondLastColMonth) {
|
||||
if (lastColMonth !== secondLastColMonth) {
|
||||
extraWidthPadding = 3;
|
||||
}
|
||||
|
||||
return extraWidthPadding;
|
||||
}
|
||||
|
||||
renderSvg(group) {
|
||||
var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group);
|
||||
return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar');
|
||||
renderSvg(container, group) {
|
||||
const width = ((group + 1) * this.daySizeWithSpace) + this.getExtraWidthPadding(group);
|
||||
return d3.select(container)
|
||||
.append('svg')
|
||||
.attr('width', width)
|
||||
.attr('height', 167)
|
||||
.attr('class', 'contrib-calendar');
|
||||
}
|
||||
|
||||
renderDays() {
|
||||
return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) {
|
||||
return function(group, i) {
|
||||
_.each(group, function(stamp, a) {
|
||||
var lastMonth, lastMonthX, month, x;
|
||||
this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g')
|
||||
.attr('transform', (group, i) => {
|
||||
_.each(group, (stamp, a) => {
|
||||
if (a === 0 && stamp.day === 0) {
|
||||
month = stamp.date.getMonth();
|
||||
x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace;
|
||||
lastMonth = _.last(_this.months);
|
||||
if (lastMonth != null) {
|
||||
lastMonthX = lastMonth.x;
|
||||
}
|
||||
if (lastMonth == null) {
|
||||
return _this.months.push({
|
||||
month: month,
|
||||
x: x
|
||||
});
|
||||
} else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) {
|
||||
return _this.months.push({
|
||||
month: month,
|
||||
x: x
|
||||
});
|
||||
const month = stamp.date.getMonth();
|
||||
const x = (this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace;
|
||||
const lastMonth = _.last(this.months);
|
||||
if (
|
||||
lastMonth == null ||
|
||||
(month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonth.x)
|
||||
) {
|
||||
this.months.push({ month, x });
|
||||
}
|
||||
}
|
||||
});
|
||||
return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)";
|
||||
};
|
||||
})(this)).selectAll('rect').data(function(stamp) {
|
||||
return stamp;
|
||||
}).enter().append('rect').attr('x', '0').attr('y', (function(_this) {
|
||||
return function(stamp, i) {
|
||||
return _this.daySizeWithSpace * stamp.day;
|
||||
};
|
||||
})(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) {
|
||||
return function(stamp) {
|
||||
var contribText, date, dateText;
|
||||
date = new Date(stamp.date);
|
||||
contribText = 'No contributions';
|
||||
if (stamp.count > 0) {
|
||||
contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : '');
|
||||
}
|
||||
dateText = date.format('mmm d, yyyy');
|
||||
return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText;
|
||||
};
|
||||
})(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) {
|
||||
return function(stamp) {
|
||||
if (stamp.count !== 0) {
|
||||
return _this.color(Math.min(stamp.count, 40));
|
||||
} else {
|
||||
return '#ededed';
|
||||
}
|
||||
};
|
||||
})(this)).attr('data-container', 'body').on('click', this.clickDay);
|
||||
return `translate(${(this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace}, 18)`;
|
||||
})
|
||||
.selectAll('rect')
|
||||
.data(stamp => stamp)
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('x', '0')
|
||||
.attr('y', stamp => this.daySizeWithSpace * stamp.day)
|
||||
.attr('width', this.daySize)
|
||||
.attr('height', this.daySize)
|
||||
.attr('fill', stamp => (
|
||||
stamp.count !== 0 ? this.color(Math.min(stamp.count, 40)) : '#ededed'
|
||||
))
|
||||
.attr('title', stamp => formatTooltipText(stamp))
|
||||
.attr('class', 'user-contrib-cell js-tooltip')
|
||||
.attr('data-container', 'body')
|
||||
.on('click', this.clickDay);
|
||||
}
|
||||
|
||||
renderDayTitles() {
|
||||
var days;
|
||||
days = [
|
||||
const days = [
|
||||
{
|
||||
text: 'M',
|
||||
y: 29 + (this.daySizeWithSpace * 1)
|
||||
y: 29 + (this.daySizeWithSpace * 1),
|
||||
}, {
|
||||
text: 'W',
|
||||
y: 29 + (this.daySizeWithSpace * 3)
|
||||
y: 29 + (this.daySizeWithSpace * 3),
|
||||
}, {
|
||||
text: 'F',
|
||||
y: 29 + (this.daySizeWithSpace * 5)
|
||||
}
|
||||
y: 29 + (this.daySizeWithSpace * 5),
|
||||
},
|
||||
];
|
||||
return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) {
|
||||
return day.y;
|
||||
}).text(function(day) {
|
||||
return day.text;
|
||||
}).attr('class', 'user-contrib-text');
|
||||
this.svg.append('g')
|
||||
.selectAll('text')
|
||||
.data(days)
|
||||
.enter()
|
||||
.append('text')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', 8)
|
||||
.attr('y', day => day.y)
|
||||
.text(day => day.text)
|
||||
.attr('class', 'user-contrib-text');
|
||||
}
|
||||
|
||||
renderMonths() {
|
||||
return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) {
|
||||
return date.x;
|
||||
}).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) {
|
||||
return function(date) {
|
||||
return _this.monthNames[date.month];
|
||||
};
|
||||
})(this));
|
||||
this.svg.append('g')
|
||||
.attr('direction', 'ltr')
|
||||
.selectAll('text')
|
||||
.data(this.months)
|
||||
.enter()
|
||||
.append('text')
|
||||
.attr('x', date => date.x)
|
||||
.attr('y', 10)
|
||||
.attr('class', 'user-contrib-text')
|
||||
.text(date => this.monthNames[date.month]);
|
||||
}
|
||||
|
||||
renderKey() {
|
||||
|
@ -169,7 +178,7 @@ export default class ActivityCalendar {
|
|||
const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
|
||||
|
||||
this.svg.append('g')
|
||||
.attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`)
|
||||
.attr('transform', `translate(18, ${(this.daySizeWithSpace * 8) + 16})`)
|
||||
.selectAll('rect')
|
||||
.data(keyColors)
|
||||
.enter()
|
||||
|
@ -185,43 +194,31 @@ export default class ActivityCalendar {
|
|||
}
|
||||
|
||||
initColor() {
|
||||
var colorRange;
|
||||
colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
|
||||
const colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
|
||||
return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange);
|
||||
}
|
||||
|
||||
initColorKey() {
|
||||
return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
|
||||
}
|
||||
|
||||
clickDay(stamp) {
|
||||
var formatted_date;
|
||||
if (this.currentSelectedDate !== stamp.date) {
|
||||
this.currentSelectedDate = stamp.date;
|
||||
formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate();
|
||||
return $.ajax({
|
||||
url: this.calendar_activities_path,
|
||||
data: {
|
||||
date: formatted_date
|
||||
},
|
||||
|
||||
const date = [
|
||||
this.currentSelectedDate.getFullYear(),
|
||||
this.currentSelectedDate.getMonth() + 1,
|
||||
this.currentSelectedDate.getDate(),
|
||||
].join('-');
|
||||
|
||||
$.ajax({
|
||||
url: this.calendarActivitiesPath,
|
||||
data: { date },
|
||||
cache: false,
|
||||
dataType: 'html',
|
||||
beforeSend: function() {
|
||||
return $('.user-calendar-activities').html('<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>');
|
||||
},
|
||||
success: function(data) {
|
||||
return $('.user-calendar-activities').html(data);
|
||||
}
|
||||
beforeSend: () => $('.user-calendar-activities').html(LOADING_HTML),
|
||||
success: data => $('.user-calendar-activities').html(data),
|
||||
});
|
||||
} else {
|
||||
this.currentSelectedDate = '';
|
||||
return $('.user-calendar-activities').html('');
|
||||
$('.user-calendar-activities').html('');
|
||||
}
|
||||
}
|
||||
|
||||
initTooltips() {
|
||||
return $('.js-contrib-calendar .js-tooltip').tooltip({
|
||||
html: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
import ActivityCalendar from './activity_calendar';
|
||||
import User from './user';
|
||||
import Cookies from 'js-cookie';
|
||||
import UserTabs from './user_tabs';
|
||||
|
||||
// use legacy exports until embedded javascript is refactored
|
||||
window.Calendar = ActivityCalendar;
|
||||
window.gl = window.gl || {};
|
||||
window.gl.User = User;
|
||||
export default function initUserProfile(action) {
|
||||
// place profile avatars to top
|
||||
$('.profile-groups-avatars').tooltip({
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/* 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();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,72 +1,76 @@
|
|||
/* 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
|
||||
* content on the Users#show page.
|
||||
*
|
||||
* ### Example Markup
|
||||
*
|
||||
* <ul class="nav-links">
|
||||
* <li class="activity-tab active">
|
||||
* <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
|
||||
* Activity
|
||||
* </a>
|
||||
* </li>
|
||||
* <li class="groups-tab">
|
||||
* <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
|
||||
* Groups
|
||||
* </a>
|
||||
* </li>
|
||||
* <li class="contributed-tab">
|
||||
* ...
|
||||
* </li>
|
||||
* <li class="projects-tab">
|
||||
* ...
|
||||
* </li>
|
||||
* <li class="snippets-tab">
|
||||
* ...
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <div class="tab-content">
|
||||
* <div class="tab-pane" id="activity">
|
||||
* Activity Content
|
||||
* </div>
|
||||
* <div class="tab-pane" id="groups">
|
||||
* Groups Content
|
||||
* </div>
|
||||
* <div class="tab-pane" id="contributed">
|
||||
* Contributed projects content
|
||||
* </div>
|
||||
* <div class="tab-pane" id="projects">
|
||||
* Projects content
|
||||
* </div>
|
||||
* <div class="tab-pane" id="snippets">
|
||||
* Snippets content
|
||||
* </div>
|
||||
* </div>
|
||||
*
|
||||
* <div class="loading-status">
|
||||
* <div class="loading">
|
||||
* Loading Animation
|
||||
* </div>
|
||||
* </div>
|
||||
*/
|
||||
|
||||
Handles persisting and restoring the current tab selection and lazily-loading
|
||||
content on the Users#show page.
|
||||
|
||||
### Example Markup
|
||||
|
||||
<ul class="nav-links">
|
||||
<li class="activity-tab active">
|
||||
<a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
|
||||
Activity
|
||||
</a>
|
||||
</li>
|
||||
<li class="groups-tab">
|
||||
<a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
|
||||
Groups
|
||||
</a>
|
||||
</li>
|
||||
<li class="contributed-tab">
|
||||
<a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed">
|
||||
Contributed projects
|
||||
</a>
|
||||
</li>
|
||||
<li class="projects-tab">
|
||||
<a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects">
|
||||
Personal projects
|
||||
</a>
|
||||
</li>
|
||||
<li class="snippets-tab">
|
||||
<a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets">
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane" id="activity">
|
||||
Activity Content
|
||||
</div>
|
||||
<div class="tab-pane" id="groups">
|
||||
Groups Content
|
||||
</div>
|
||||
<div class="tab-pane" id="contributed">
|
||||
Contributed projects content
|
||||
</div>
|
||||
<div class="tab-pane" id="projects">
|
||||
Projects content
|
||||
</div>
|
||||
<div class="tab-pane" id="snippets">
|
||||
Snippets content
|
||||
</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 class="loading-status">
|
||||
<div class="loading">
|
||||
Loading Animation
|
||||
</div>
|
||||
</div>
|
||||
*/
|
||||
`;
|
||||
|
||||
export default class UserTabs {
|
||||
constructor ({ defaultAction, action, parentEl }) {
|
||||
constructor({ defaultAction, action, parentEl }) {
|
||||
this.loaded = {};
|
||||
this.defaultAction = defaultAction || 'activity';
|
||||
this.action = action || this.defaultAction;
|
||||
this.$parentEl = $(parentEl) || $(document);
|
||||
this._location = window.location;
|
||||
this.windowLocation = window.location;
|
||||
this.$parentEl.find('.nav-links a')
|
||||
.each((i, navLink) => {
|
||||
this.loaded[$(navLink).attr('data-action')] = false;
|
||||
|
@ -82,12 +86,10 @@ export default class UserTabs {
|
|||
}
|
||||
|
||||
bindEvents() {
|
||||
this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this);
|
||||
|
||||
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));
|
||||
|
||||
this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper);
|
||||
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('click', '.gl-pagination a', event => this.changeProjectsPage(event));
|
||||
}
|
||||
|
||||
changeProjectsPage(e) {
|
||||
|
@ -122,7 +124,7 @@ export default class UserTabs {
|
|||
|
||||
const loadableActions = ['groups', 'contributed', 'projects', 'snippets'];
|
||||
if (loadableActions.indexOf(action) > -1) {
|
||||
return this.loadTab(action, endpoint);
|
||||
this.loadTab(action, endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,25 +133,38 @@ export default class UserTabs {
|
|||
beforeSend: () => this.toggleLoading(true),
|
||||
complete: () => this.toggleLoading(false),
|
||||
dataType: 'json',
|
||||
type: 'GET',
|
||||
url: endpoint,
|
||||
success: (data) => {
|
||||
const tabSelector = `div#${action}`;
|
||||
this.$parentEl.find(tabSelector).html(data.html);
|
||||
this.loaded[action] = true;
|
||||
return gl.utils.localTimeAgo($('.js-timeago', tabSelector));
|
||||
}
|
||||
gl.utils.localTimeAgo($('.js-timeago', tabSelector));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
loadActivities() {
|
||||
if (this.loaded['activity']) {
|
||||
if (this.loaded.activity) {
|
||||
return;
|
||||
}
|
||||
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();
|
||||
return this.loaded['activity'] = true;
|
||||
this.loaded.activity = true;
|
||||
}
|
||||
|
||||
toggleLoading(status) {
|
||||
|
@ -158,13 +173,13 @@ export default class UserTabs {
|
|||
}
|
||||
|
||||
setCurrentAction(source) {
|
||||
let new_state = source;
|
||||
new_state = new_state.replace(/\/+$/, '');
|
||||
new_state += this._location.search + this._location.hash;
|
||||
let newState = source;
|
||||
newState = newState.replace(/\/+$/, '');
|
||||
newState += this.windowLocation.search + this.windowLocation.hash;
|
||||
history.replaceState({
|
||||
url: new_state
|
||||
}, document.title, new_state);
|
||||
return new_state;
|
||||
url: newState,
|
||||
}, document.title, newState);
|
||||
return newState;
|
||||
}
|
||||
|
||||
getCurrentAction() {
|
||||
|
|
|
@ -73,10 +73,7 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
def calendar
|
||||
calendar = contributions_calendar
|
||||
@activity_dates = calendar.activity_dates
|
||||
|
||||
render 'calendar', layout: false
|
||||
render json: contributions_calendar.activity_dates
|
||||
end
|
||||
|
||||
def calendar_activities
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
.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 @@
|
|||
- @hide_breadcrumbs = true
|
||||
- page_title @user.name
|
||||
- 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)
|
||||
- @no_container = true
|
||||
|
||||
|
@ -107,7 +104,7 @@
|
|||
.tab-content
|
||||
#activity.tab-pane
|
||||
.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
|
||||
%i.fa.fa-spinner.fa-spin
|
||||
.user-calendar-activities
|
||||
|
@ -131,10 +128,3 @@
|
|||
|
||||
.loading-status
|
||||
= spinner
|
||||
|
||||
:javascript
|
||||
var userProfile;
|
||||
|
||||
userProfile = new gl.User({
|
||||
action: "#{controller.action_name}"
|
||||
});
|
||||
|
|
|
@ -67,7 +67,6 @@ var config = {
|
|||
stl_viewer: './blob/stl_viewer.js',
|
||||
terminal: './terminal/terminal_bundle.js',
|
||||
u2f: ['vendor/u2f'],
|
||||
users: './users/index.js',
|
||||
raven: './raven/index.js',
|
||||
vue_merge_request_widget: './vue_merge_request_widget/index.js',
|
||||
test: './test.js',
|
||||
|
@ -185,7 +184,6 @@ var config = {
|
|||
name: 'common_d3',
|
||||
chunks: [
|
||||
'graphs',
|
||||
'users',
|
||||
'monitoring',
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -80,9 +80,9 @@ describe UsersController do
|
|||
it 'renders calendar' do
|
||||
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
|
||||
|
||||
context 'forked project' do
|
||||
|
|
Loading…
Reference in a new issue