Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
28b5bb94
Commit
28b5bb94
authored
Nov 30, 2016
by
Bryce Johnson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Wire up stores and services base.
parent
1905650a
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
214 additions
and
111 deletions
+214
-111
app/assets/javascripts/merge_request_widget/approvals/components/approvals_body.js.es6
...request_widget/approvals/components/approvals_body.js.es6
+34
-16
app/assets/javascripts/merge_request_widget/approvals/components/approvals_footer.js.es6
...quest_widget/approvals/components/approvals_footer.js.es6
+20
-9
app/assets/javascripts/merge_request_widget/approvals/services/approvals_api.js.es6
...ge_request_widget/approvals/services/approvals_api.js.es6
+68
-0
app/assets/javascripts/merge_request_widget/approvals/services/approvals_service.js.es6
...equest_widget/approvals/services/approvals_service.js.es6
+0
-24
app/assets/javascripts/merge_request_widget/approvals/stores/approvals_store.js.es6
...ge_request_widget/approvals/stores/approvals_store.js.es6
+30
-3
app/assets/javascripts/merge_request_widget/services/store_base.js.es6
...vascripts/merge_request_widget/services/store_base.js.es6
+0
-0
app/assets/javascripts/merge_request_widget/stores/widget_store.js.es6
...vascripts/merge_request_widget/stores/widget_store.js.es6
+38
-23
app/assets/javascripts/merge_request_widget/widget_bundle.js.es6
...ets/javascripts/merge_request_widget/widget_bundle.js.es6
+16
-31
app/views/projects/merge_requests/widget/_open.html.haml
app/views/projects/merge_requests/widget/_open.html.haml
+8
-5
No files found.
app/assets/javascripts/merge_request_widget/approvals/components/approvals_body.js.es6
View file @
28b5bb94
//= require ../services/approvals_api
(() => {
// can the user edit this MR
// has this user approved this MR
// What is the info about users who can approve this MR but have not yet?
// How many approvals are needed?
// How many approvals are had?
gl.MergeRequestWidget.approvalsBody = {
const app = gl.MergeRequestWidget;
const api = gl.MergeRequestWidget.ApprovalsApi;
const componentRegistry = app.Components || (app.Components = {});
componentRegistry.approvalsBody = {
name: 'ApprovalsBody',
props: ['approverNames', 'approvalsLeft', 'canApprove'],
data() {
return {
loading: true,
};
},
computed: {
approvalsRequiredStringified() {
return this.approvalsLeft === 1 ?
"one more approval"
:
return this.approvalsLeft === 1 ?
'one more approval'
:
`${this.approvalsLeft} more approvals`;
},
approverNamesStringified() {
const lastIdx = this.approverNames.length - 1;
return this.approverNames.reduce((memo, curr, index) => {
return index !== lastIdx ? `${memo} ${curr}, ` : `${memo} or ${curr}`;
const newList = index !== lastIdx ? `${memo} ${curr}, ` :
`${memo} or ${curr}`;
return newList;
}, '');
}
},
}
,
},
methods: {
approveMergeRequest() {
approvalsService
.approveMergeRequest();
return api
.approveMergeRequest();
},
},
beforeCreate() {
approvalsService.fetchApprovals();
api.fetchApprovals().then(() => {
this.loading = false;
});
},
template: `
<div>
<h4> Requires {{ approvalsRequiredStringified }} (from {{ approverNamesStringified }})</h4>
<div class="append-bottom-10">
<button v-if='canApprove' @click='approveMergeRequest' class="btn btn-primary approve-btn">Approve Merge Request</button>
<div>
<h4> Requires {{ approvalsRequiredStringified }} (from {{ approverNamesStringified }})</h4>
<div class='append-bottom-10'>
<button
v-if='canApprove'
@click='approveMergeRequest'
class='btn btn-primary approve-btn'>
Approve Merge Request
</button>
</div>
</div>
<loading-icon v-if='loading'></loading-icon>
</div>
`,
};
})();
app/assets/javascripts/merge_request_widget/approvals/components/approvals_footer.js.es6
View file @
28b5bb94
//= require ../services/approvals_api
//= require vue_common_component/link_to_member_avatar
//= require vue_common_component/loading_icon
(() => {
// does the user have permission to edit this thing
// has the user already approved this or not
// what are the users who have approved this already, with all their info
gl.MergeRequestWidget.approvalsFooter = {
const app = gl.MergeRequestWidget;
const api = gl.MergeRequestWidget.ApprovalsApi;
const componentRegistry = app.Components || (app.Components = {});
componentRegistry.approvalsFooter = {
name: 'ApprovalsFooter',
props: ['canUpdateMergeRequest', 'hasApprovedMergeRequest', 'approvedByUsers'],
data() {
return {
loading: true,
};
},
methods: {
removeApproval() {
approvalsService
.unapproveMergeRequest();
}
return api
.unapproveMergeRequest();
}
,
},
beforeCreate() {
approvalsService.fetchApprovals();
api.fetchApprovals().then(() => {
this.loading = false;
});
},
template: `
<div>
...
...
@@ -26,9 +37,9 @@
<span>
<i class='fa fa-close'></i>
<button @click='removeApproval'>Remove your approval</button>
{{ approvedByUsers[0].name }}
</span>
<loading-icon v-if='loading'></loading-icon>
</div>
`
`
,
};
})();
app/assets/javascripts/merge_request_widget/approvals/services/approvals_api.js.es6
0 → 100644
View file @
28b5bb94
//= require ../stores/approvals_store
// TODO: Determine whether component-specific store should be accessed here as a member
((MergeRequestWidget) => {
function mockApprovalRequest(payload) {
const currentPayload = Object.assign({}, payload.data);
const currentStore = gl.MergeRequestWidget.Store.data.approvals;
const parsedStore = JSON.parse(JSON.stringify(currentStore));
const mockResp = Object.assign(currentPayload, parsedStore);
return new Promise((resolve) => {
setTimeout(() => {
resolve(mockResp);
}, 100);
});
}
class ApprovalsApi {
constructor() {
this.store = null;
this.isFetching = false;
this.hasBeenFetched = true;
}
fetchApprovals() {
const payload = {
type: 'GET',
};
return this.genericApprovalRequest(payload);
}
approveMergeRequest() {
const payload = {
type: 'PUT',
data: {
approve: true,
},
};
return this.genericApprovalRequest(payload);
}
unapproveMergeRequest() {
const payload = {
type: 'PUT',
data: {
approve: false,
},
};
return this.genericApprovalRequest(payload);
}
genericApprovalRequest(payload = {}) {
return mockApprovalRequest(payload)
.then(resp => this.updateStore(resp))
.catch((err) => {
throw err;
});
}
updateStore(newState = {}) {
this.store = gl.MergeRequestWidget.Store.data.approvals; // Always update shared store
return Object.assign(this.store, newState);
}
}
gl.MergeRequestWidget.ApprovalsApi = new ApprovalsApi();
})(gl.MergeRequestWidget || (gl.MergeRequestWidget = {}));
app/assets/javascripts/merge_request_widget/approvals/services/approvals_service.js.es6
deleted
100644 → 0
View file @
1905650a
//= require ../stores/approvals_store
(() => {
class ApprovalsService {
constructor() {
this.isFetching = false;
this.hasBeenFetched = true;
}
fetchApprovals() {
return makeRequest().then((data) => {
this.isFetching = false;
this.hasBeenFetched = true;
return data;
});
}
approveMergeRequest(payload) {
return payload;
}
unapproveMergeRequest(payload) {
return payload;
}
}
gl.mergeRequestWidget.ApprovalsService = ApprovalsService;
})();
\ No newline at end of file
app/assets/javascripts/merge_request_widget/approvals/stores/approvals_store.js.es6
View file @
28b5bb94
(()=>{
})()
\ No newline at end of file
(() => {
let singleton;
class ApprovalsStore {
constructor(rootEl) {
if (!singleton) {
singleton = gl.MergeRequestWidget.ApprovalsStore = this;
this.init(rootEl);
}
return singleton;
}
init(rootEl) {
this.data = {};
const dataset = rootEl.dataset;
this.assignToData({
approvedByUsers: JSON.parse(dataset.approvedByUsers),
approverNames: JSON.parse(dataset.approverNames),
approvalsLeft: Number(dataset.approvalsLeft),
moreApprovals: Number(dataset.moreApprovals),
});
}
assignToData(val) {
Object.assign(this.data, val);
}
}
gl.MergeRequestWidget.ApprovalsStore = ApprovalsStore;
})();
app/assets/javascripts/merge_request_widget/services/store_base.js.es6
0 → 100644
View file @
28b5bb94
app/assets/javascripts/merge_request_widget/stores/widget_store.js.es6
View file @
28b5bb94
(() => {
//= require ../approvals/stores/approvals_store
((MergeRequestWidget) => {
let singleton;
class MergeRequestWidgetStore {
constructor(el) {
this.dataset = el.dataset;
constructor(rootEl) {
if (!singleton) {
singleton = MergeRequestWidget.Store = this;
this.init(rootEl);
}
return singleton;
// TODO: add the following to the root el dataset: approvedByUsers,
// approverNames, approvalsNeeded, canUpdateMergeRequest, endpoint
}
init(rootEl) {
this.rootEl = rootEl;
this.dataset = rootEl.dataset;
this.data = {};
// TODO: Break each into their own store
this.initResource();
this.initPermissions();
this.initApprovals();
}
/* General Resources */
initResource() {
Object.assign(this.data, {
resource: {
endpoint: this.dataset.endpoint
}
this.assignToData('resource', {
endpoint: 'my/endpoint',
});
}
initPermissions() {
Object.assign(this.data, {
permissions: {
canApprove: Boolean(this.dataset.canApprove)
}
this.assignToData('permissions', {
canUpdateMergeRequest: Boolean(this.dataset.canUpdateMergeRequest),
});
}
/* Component-specific */
initApprovals() {
const dataset = this.dataset;
Object.assign(this.data, {
approvals: {
approvedByUsers: JSON.parse(dataset.approvedByUsers),
approverNames: JSON.parse(dataset.approverNames),
approvalsLeft: Number(dataset.approvalsLeft),
moreApprovals: Number(dataset.approvalsLeft),
}
});
const approvalsStore = new gl.MergeRequestWidget.ApprovalsStore(this.rootEl);
this.assignToData('approvals', approvalsStore.data);
}
assignToData(key, val) {
this.data[key] = {};
Object.assign(this.data[key], val);
}
}
gl.MergeRequestWidgetStore = MergeRequestWidgetStore;
})()
\ No newline at end of file
MergeRequestWidget.Store = MergeRequestWidgetStore;
})(gl.MergeRequestWidget || (MergeRequestWidget = {}));
app/assets/javascripts/merge_request_widget/widget_bundle.js.es6
View file @
28b5bb94
//= require ./stores/widget_store
//= require ./services/widget_service
//= require ./approvals/approvals_bundle
/**
*
* 'data-approved-by-users' => @merge_request.approved_by_users.to_json, 'data-approver-names' => @merge_request.approvers.to_json,'data-approvals-left' => @merge_request.approvals_left, 'data-more-approvals' => (@merge_request.approvals_left - @merge_request.approvers_left.count), 'data-can-approve' => @merge_request.user_is_approver(current_user), 'data-endpoint'=> '/myendpoint/tho' }
*
*/
$(() => {
// Move initialization to somewhere else -- all can be conducted before DOM ready
new gl.MergeRequestWidgetStore();
new gl.MergeRequestWidgetService();
const app = gl.MergeRequestWidget;
new Vue({
el: '#merge-request-widget-app',
data: Store.data,
components: {
'approvals-body': app.approvalsBody,
'approvals-footer': app.approvalsFooter
}
((MergeRequestWidget) => {
$(() => {
const rootEl = document.getElementById('merge-request-widget-app');
const store = new MergeRequestWidget.Store(rootEl);
const components = MergeRequestWidget.Components;
MergeRequestWidget.App = new Vue({
el: rootEl,
data: store.data,
components: {
'approvals-body': components.approvalsBody,
'approvals-footer': components.approvalsFooter,
},
});
});
});
/**
*
* gl.MergeRequestWidgetApp
* Store
* ApiService
*
*
* TODO: Add documentation for registering new widget components
*
*/
// Need list of approved by, list of those who can approve, and the number required
\ No newline at end of file
})(gl.MergeRequestWidget || (gl.MergeRequestWidget = {}));
app/views/projects/merge_requests/widget/_open.html.haml
View file @
28b5bb94
#merge-request-widget-app
.mr-state-widget
-
approvers_names
=
@merge_request
.
approvers_left
.
map
(
&
:name
).
to_json
-
more_approvals
=
@merge_request
.
approvals_left
-
@merge_request
.
approvers_left
.
count
-
approvals_left
=
@merge_request
.
approvals_left
#merge-request-widget-app
.mr-state-widget
{
'data-approved-by-users'
=>
@merge_request
.
approved_by_users
.
to_json
,
'data-approver-names'
=>
approvers_names
,
'data-approvals-left'
=>
approvals_left
,
'data-more-approvals'
=>
more_approvals
,
'data-endpoint'
=>
'/myendpoint/tho'
}
=
render
'projects/merge_requests/widget/heading'
.mr-widget-body
-# After conflicts are resolved, the user is redirected back to the MR page.
...
...
@@ -24,8 +28,7 @@
-
elsif
@merge_request
.
work_in_progress?
=
render
'projects/merge_requests/widget/open/wip'
-
elsif
@merge_request
.
requires_approve?
&&
!
@merge_request
.
approved?
%approvals-body
{
'@give-approval'
=>
'approveMergeRequest'
,
'@fetch-approvals'
=>
'fetchApprovals'
,
:approver
-
names
' => '
approvals
.
approverNames
', '
:approvals
-
left
' => '
approvals
.
approvalsLeft
', '
:more
-
approvals
' => '
approvals
.
moreApprovals
', '
:can
-
approve
' => '
permissions
.
canApprove
', '
:needs
-
approval
'
=>
(
@merge_request
.
requires_approve?
&&
!
@merge_request
.
approved?
)
}
=
icon
(
'spinner spin'
,
class:
'loading-icon'
)
%approvals-body
{
':approver-names'
=>
'approvals.approverNames'
,
':approvals-left'
=>
'approvals.approvalsLeft'
,
':canApprove'
=>
'approvals.canApprove'
}
-
elsif
@merge_request
.
merge_when_build_succeeds?
=
render
'projects/merge_requests/widget/open/merge_when_build_succeeds'
-
elsif
!
@merge_request
.
can_be_merged_by?
(
current_user
)
...
...
@@ -50,6 +53,6 @@
-
if
@merge_request
.
approvals
.
any?
.mr-widget-footer.approved-by-users
%approvals-footer
{
'@remove-approval'
=>
'unapproveMergeRequest'
,
'@fetch-approvals'
=>
'fetchApprovals'
,
':approved-by-users'
=>
'approvals.approvedByUsers'
,
':approver-names'
=>
'approvals.approverNames'
,
':approvals-left'
=>
'approvals.approvalsLeft'
,
':more-approvals'
=>
'approvals.moreApprovals'
,
':can-approve'
=>
'permissions.canApprove'
,
'has-approvals'
=>
@merge_request
.
approvals
.
any?
}
=
icon
(
'spinner spin'
,
class:
'loading-icon'
'
)
%approvals-footer
{
':approver-names'
=>
'approvals.approverNames'
,
':approvals-left'
=>
'approvals.approvalsLeft'
,
':canApprove'
=>
'approvals.canApprove'
,
':approved-by-users'
=>
'approvals.approvedByUsers'
}
=
icon
(
'spinner spin'
,
class:
'loading-icon'
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment