Skip to content

Commit 54c63f4

Browse files
committed
Merge remote-tracking branch 'upstream/master' into pr/740
2 parents 7dec3d1 + 086249c commit 54c63f4

File tree

32 files changed

+1113
-176
lines changed

32 files changed

+1113
-176
lines changed

app/adapters/api-token.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.RESTAdapter.extend({
4+
namespace: 'me',
5+
pathForType() {
6+
return 'tokens';
7+
}
8+
});

app/components/api-token-row.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Ember from 'ember';
2+
3+
export default Ember.Component.extend({
4+
emptyName: Ember.computed.empty('api_token.name'),
5+
disableCreate: Ember.computed.or('api_token.isSaving', 'emptyName'),
6+
serverError: null,
7+
8+
didInsertElement() {
9+
if (this.get('api_token.isNew')) {
10+
this.$('input').focus();
11+
}
12+
},
13+
14+
actions: {
15+
saveToken() {
16+
this.get('api_token')
17+
.save()
18+
.then(() => this.set('serverError', null))
19+
.catch(err => {
20+
let msg;
21+
if (err.errors && err.errors[0] && err.errors[0].detail) {
22+
msg = `An error occurred while saving this token, ${err.errors[0].detail}`;
23+
} else {
24+
msg = 'An unknown error occurred while saving this token';
25+
}
26+
this.set('serverError', msg);
27+
});
28+
},
29+
revokeToken() {
30+
this.get('api_token')
31+
.destroyRecord()
32+
.catch(err => {
33+
let msg;
34+
if (err.errors && err.errors[0] && err.errors[0].detail) {
35+
msg = `An error occurred while revoking this token, ${err.errors[0].detail}`;
36+
} else {
37+
msg = 'An unknown error occurred while revoking this token';
38+
}
39+
this.set('serverError', msg);
40+
});
41+
},
42+
}
43+
});

app/controllers/me/index.js

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,24 @@ import Ember from 'ember';
33
const { inject: { service } } = Ember;
44

55
export default Ember.Controller.extend({
6+
tokenSort: ['created_at:desc'],
7+
8+
sortedTokens: Ember.computed.sort('model.api_tokens', 'tokenSort'),
69

710
ajax: service(),
811

912
flashMessages: service(),
1013

1114
isResetting: false,
1215

16+
newTokens: Ember.computed.filterBy('model.api_tokens', 'isNew', true),
17+
disableCreate: Ember.computed.notEmpty('newTokens'),
18+
1319
actions: {
14-
resetToken() {
15-
this.set('isResetting', true);
16-
17-
this.get('ajax').put('/me/reset_token').then((d) => {
18-
this.get('model').set('api_token', d.api_token);
19-
}).catch((reason) => {
20-
let msg;
21-
if (reason.status === 403) {
22-
msg = 'A login is required to perform this action';
23-
} else {
24-
msg = 'An unknown error occurred';
25-
}
26-
this.get('flashMessages').queue(msg);
27-
// TODO: this should be an action, the route state machine
28-
// should receive signals not external transitions
29-
this.transitionToRoute('index');
30-
}).finally(() => {
31-
this.set('isResetting', false);
20+
startNewToken() {
21+
this.get('store').createRecord('api-token', {
22+
created_at: new Date(Date.now() + 2000),
3223
});
33-
}
24+
},
3425
}
3526
});

app/index.html

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
11
<!DOCTYPE html>
2-
<html>
2+
<html lang="en">
33
<head>
44
<meta charset="utf-8">
55
<meta http-equiv="X-UA-Compatible" content="IE=edge">
66
<meta name="viewport" content="width=device-width, initial-scale=1">
77

88
{{content-for 'head'}}
99

10-
<script>
11-
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
12-
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
13-
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
14-
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
15-
16-
ga('create', 'UA-58390457-3', 'auto');
17-
ga('send', 'pageview');
18-
</script>
19-
2010
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
2111
<link rel="stylesheet" href="{{rootURL}}assets/cargo.css">
2212

app/mixins/google-pageview.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import Ember from 'ember';
22

33
export default Ember.Mixin.create({
4+
5+
metrics: Ember.inject.service(),
6+
47
notifyGoogleAnalytics: Ember.on('didTransition', function() {
5-
if (!window.ga) {
6-
return;
7-
}
8-
return window.ga('send', 'pageview', {
9-
page: this.get('url'),
10-
title: this.get('url')
8+
Ember.run.scheduleOnce('afterRender', this, () => {
9+
const page = this.get('url');
10+
const title = this.get('url');
11+
Ember.get(this, 'metrics').trackPage({ page, title });
1112
});
1213
})
1314
});

app/models/api-token.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.Model.extend({
4+
name: DS.attr('string'),
5+
token: DS.attr('string'),
6+
created_at: DS.attr('date'),
7+
last_used_at: DS.attr('date'),
8+
});

app/models/user.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ export default DS.Model.extend({
44
email: DS.attr('string'),
55
name: DS.attr('string'),
66
login: DS.attr('string'),
7-
api_token: DS.attr('string'),
87
avatar: DS.attr('string'),
98
url: DS.attr('string'),
109
kind: DS.attr('string'),

app/routes/application.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ export default Ember.Route.extend({
1212
if (this.session.get('isLoggedIn') &&
1313
this.session.get('currentUser') === null) {
1414
this.get('ajax').request('/me').then((response) => {
15-
let user = this.store.push(this.store.normalize('user', response.user));
16-
user.set('api_token', response.api_token);
17-
this.session.set('currentUser', user);
15+
this.session.set('currentUser', this.store.push(this.store.normalize('user', response.user)));
1816
}).catch(() => this.session.logoutUser()).finally(() => {
1917
window.currentUserDetected = true;
2018
Ember.$(window).trigger('currentUserDetected');

app/routes/login.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ export default Ember.Route.extend({
6767
}
6868

6969
let user = this.store.push(this.store.normalize('user', data.user));
70-
user.set('api_token', data.api_token);
7170
let transition = this.session.get('savedTransition');
7271
this.session.loginUser(user);
7372
if (transition) {

app/routes/me/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import AuthenticatedRoute from '../../mixins/authenticated-route';
33

44
export default Ember.Route.extend(AuthenticatedRoute, {
55
model() {
6-
return this.session.get('currentUser');
6+
return {
7+
user: this.session.get('currentUser'),
8+
api_tokens: this.get('store').findAll('api-token'),
9+
};
710
},
811
});

app/serializers/api-token.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.RESTSerializer.extend({
4+
payloadKeyFromModelName() {
5+
return 'api_token';
6+
}
7+
});

app/styles/home.scss

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
@mixin button($start, $end) {
2-
$s2: darken($start, 5%);
3-
$e2: darken($end, 5%);
2+
$start_dark: darken($start, 5%);
3+
$end_dark: darken($end, 5%);
4+
$start_light: lighten($start, 5%);
5+
$end_light: lighten($end, 5%);
6+
47
padding: 15px 40px;
58
display: inline-block;
69
color: $main-color;
@@ -17,15 +20,24 @@
1720
margin-right: 10px;
1821
}
1922

20-
&:hover { @include vertical-gradient($s2, $e2); outline: 0; }
21-
&.active { @include vertical-gradient($s2, $e2); outline: 0; }
23+
&:hover { @include vertical-gradient($start_dark, $end_dark); outline: 0; }
24+
&.active { @include vertical-gradient($start_dark, $end_dark); outline: 0; }
25+
&[disabled] {
26+
@include vertical-gradient($start_light, $end_light);
27+
color: $main-color-light;
28+
}
2229
}
2330

2431
.yellow-button {
2532
@include button(#fede9e, #fdc452);
2633
vertical-align: middle;
2734
}
2835

36+
button.small {
37+
padding: 10px 20px;
38+
@include border-radius(30px);
39+
}
40+
2941
.tan-button {
3042
@include button(rgb(232, 227, 199), rgb(214, 205, 153));
3143
}

app/styles/me.scss

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
}
8888
}
8989

90-
#stats{
90+
#stats {
9191
margin-left: auto;
9292
padding: 10px;
9393

@@ -100,4 +100,67 @@
100100
@include display-flex;
101101
@include align-items(center);
102102
}
103-
}
103+
}
104+
105+
.me-subheading {
106+
@include display-flex;
107+
.right {
108+
@include flex(2);
109+
@include display-flex;
110+
@include justify-content(flex-end);
111+
@include align-self(center);
112+
}
113+
}
114+
115+
#tokens {
116+
background-color: $main-bg-dark;
117+
@include display-flex;
118+
@include flex-direction(column);
119+
.row {
120+
width: 100%;
121+
border: 1px solid #d5d3cb;
122+
border-bottom-width: 0px;
123+
&:last-child { border-bottom-width: 1px; }
124+
padding: 10px 20px;
125+
@include display-flex;
126+
@include align-items(center);
127+
.name {
128+
@include flex(1);
129+
margin-right: 0.4em;
130+
font-weight: bold;
131+
}
132+
.dates {
133+
@include flex(content);
134+
@include display-flex;
135+
@include flex-direction(column);
136+
@include align-items(flex-end);
137+
margin-right: 0.4em;
138+
}
139+
.actions {
140+
@include display-flex;
141+
@include align-items(center);
142+
img { margin-left: 10px }
143+
}
144+
}
145+
.create-token {
146+
.name {
147+
input {
148+
width: 100%;
149+
}
150+
padding-right: 20px;
151+
margin-right: 0px;
152+
}
153+
background-color: $main-bg-dark;
154+
}
155+
.new-token {
156+
border-top-width: 0px;
157+
@include flex-direction(column);
158+
@include justify-content(stretch);
159+
}
160+
.error {
161+
border-top-width: 0px;
162+
font-weight: bold;
163+
color: rgb(216, 0, 41);
164+
padding: 0px 10px 10px 20px;
165+
}
166+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<div class={{if api_token.isNew "row create-token" "row"}}>
2+
<div class='name'>
3+
{{#if api_token.isNew}}
4+
{{input
5+
type="text"
6+
placeholder="New token name"
7+
disabled=api_token.isSaving
8+
value=api_token.name
9+
autofocus=true
10+
enter="saveToken"}}
11+
{{else}}
12+
{{ api_token.name }}
13+
{{/if}}
14+
</div>
15+
16+
<div class='spacer'></div>
17+
18+
{{#unless api_token.isNew}}
19+
<div class='dates'>
20+
<div class='created-at'>
21+
<span class='small' title={{api_token.created_at}}>
22+
Created {{moment-from-now api_token.created_at}}
23+
</span>
24+
</div>
25+
{{#if api_token.last_used_at}}
26+
<div class='last_used_at'>
27+
<span class='small' title={{api_token.last_used_at}}>
28+
Last used {{moment-from-now api_token.last_used_at}}
29+
</span>
30+
</div>
31+
{{else}}
32+
<div class='last_used_at'>
33+
<span class='small'>Never used</span>
34+
</div>
35+
{{/if}}
36+
</div>
37+
{{/unless}}
38+
39+
<div class='actions'>
40+
{{#if api_token.isNew}}
41+
<button class='small yellow-button'
42+
disabled={{disableCreate}}
43+
title={{if emptyName "You must specify a name" ""}}
44+
{{action "saveToken"}}>Create</button>
45+
{{else}}
46+
<button class='small tan-button'
47+
disabled={{api_token.isSaving}}
48+
{{action "revokeToken"}}>Revoke</button>
49+
{{/if}}
50+
{{#if api_token.isSaving}}
51+
<img class='overlay' src="/assets/ajax-loader.gif" />
52+
{{/if}}
53+
</div>
54+
</div>
55+
56+
{{#if serverError}}
57+
<div class='row error'>
58+
<div>
59+
{{ serverError }}
60+
</div>
61+
</div>
62+
{{/if}}
63+
64+
{{#if api_token.token}}
65+
<div class='row new-token'>
66+
<div>
67+
Please record this token somewhere, you cannot retrieve
68+
its value again. For use on the command line you can save it to <code>~/.cargo/config</code>
69+
with:
70+
71+
<pre>cargo login {{ api_token.token }}</pre>
72+
</div>
73+
</div>
74+
{{/if}}

0 commit comments

Comments
 (0)