File tree Expand file tree Collapse file tree 15 files changed +308
-32
lines changed
controllers/settings/tokens
tests/routes/settings/tokens Expand file tree Collapse file tree 15 files changed +308
-32
lines changed Original file line number Diff line number Diff line change 27
27
28
28
<dd .Menu local-class =" current-user-links" as |menu|>
29
29
<menu .Item><LinkTo @route =" dashboard" >Dashboard</LinkTo ></menu .Item>
30
- <menu .Item><LinkTo @route =" settings" >Account Settings</LinkTo ></menu .Item>
30
+ <menu .Item><LinkTo @route =" settings" data-test-settings >Account Settings</LinkTo ></menu .Item>
31
31
<menu .Item><LinkTo @route =" me.pending-invites" >Owner Invites</LinkTo ></menu .Item>
32
32
<menu .Item local-class =" menu-item-with-separator" >
33
33
<button
Original file line number Diff line number Diff line change 1
1
<div local-class =" page" ...attributes>
2
- <SideMenu as |menu|>
2
+ <SideMenu data-test-settings-menu as |menu|>
3
3
<menu .Item @link ={{ link " settings.profile" }} >Profile</menu .Item>
4
4
{{ #if this.design.showToggleButton }}
5
5
<menu .Item @link ={{ link " settings.appearance" }} >Appearance</menu .Item>
6
6
{{ /if }}
7
7
<menu .Item @link ={{ link " settings.email-notifications" }} >Email Notifications</menu .Item>
8
- <menu .Item @link ={{ link " settings.tokens" }} >API Tokens</menu .Item>
8
+ <menu .Item @link ={{ link " settings.tokens" }} data-test-tokens >API Tokens</menu .Item>
9
9
</SideMenu >
10
10
11
11
<div local-class =" content" >
Original file line number Diff line number Diff line change @@ -8,15 +8,20 @@ import { task } from 'ember-concurrency';
8
8
export default class ApiTokens extends Component {
9
9
@service store ;
10
10
@service notifications ;
11
+ @service router ;
11
12
12
13
@tracked newToken ;
13
14
14
15
get sortedTokens ( ) {
15
16
return this . args . tokens . filter ( t => ! t . isNew ) . sort ( ( a , b ) => ( a . created_at < b . created_at ? 1 : - 1 ) ) ;
16
17
}
17
18
18
- @action startNewToken ( ) {
19
- this . newToken = this . store . createRecord ( 'api-token' ) ;
19
+ @action startNewToken ( event ) {
20
+ if ( event . altKey ) {
21
+ this . router . transitionTo ( 'settings.tokens.new' ) ;
22
+ } else {
23
+ this . newToken = this . store . createRecord ( 'api-token' ) ;
24
+ }
20
25
}
21
26
22
27
saveTokenTask = task ( async ( ) => {
Original file line number Diff line number Diff line change 1
- <li >
1
+ <li ...attributes >
2
2
<a href ={{ @link.url }} local-class =" link {{ if @link.isActive " active" }} " {{ on " click" @link.transitionTo }} >{{ yield }} </a >
3
3
</li >
Original file line number Diff line number Diff line change
1
+ import Controller from '@ember/controller' ;
2
+ import { action } from '@ember/object' ;
3
+ import { inject as service } from '@ember/service' ;
4
+ import { tracked } from '@glimmer/tracking' ;
5
+
6
+ import { task } from 'ember-concurrency' ;
7
+
8
+ export default class NewTokenController extends Controller {
9
+ @service notifications ;
10
+ @service sentry ;
11
+ @service store ;
12
+ @service router ;
13
+
14
+ @tracked name ;
15
+ @tracked nameInvalid ;
16
+
17
+ constructor ( ) {
18
+ super ( ...arguments ) ;
19
+ this . reset ( ) ;
20
+ }
21
+
22
+ saveTokenTask = task ( async ( ) => {
23
+ let { name } = this ;
24
+ if ( ! name ) {
25
+ this . nameInvalid = true ;
26
+ return ;
27
+ }
28
+
29
+ let token = this . store . createRecord ( 'api-token' , { name } ) ;
30
+
31
+ try {
32
+ // Save the new API token on the backend
33
+ await token . save ( ) ;
34
+ // Reset the form
35
+ this . reset ( ) ;
36
+ // Navigate to the API token list
37
+ this . router . transitionTo ( 'settings.tokens.index' ) ;
38
+ } catch ( error ) {
39
+ // Notify the user
40
+ this . notifications . error ( 'An error has occurred while generating your API token. Please try again later!' ) ;
41
+ // Notify the crates.io team
42
+ this . sentry . captureException ( error ) ;
43
+ // Notify the developer
44
+ console . error ( error ) ;
45
+ }
46
+ } ) ;
47
+
48
+ reset ( ) {
49
+ this . name = '' ;
50
+ this . nameInvalid = false ;
51
+ }
52
+
53
+ @action resetNameValidation ( ) {
54
+ this . nameInvalid = false ;
55
+ }
56
+ }
Original file line number Diff line number Diff line change @@ -34,7 +34,9 @@ Router.map(function () {
34
34
this . route ( 'appearance' ) ;
35
35
this . route ( 'email-notifications' ) ;
36
36
this . route ( 'profile' ) ;
37
- this . route ( 'tokens' ) ;
37
+ this . route ( 'tokens' , function ( ) {
38
+ this . route ( 'new' ) ;
39
+ } ) ;
38
40
} ) ;
39
41
this . route ( 'user' , { path : '/users/:user_id' } ) ;
40
42
this . route ( 'install' ) ;
Original file line number Diff line number Diff line change 1
- import { inject as service } from '@ember/service' ;
2
-
3
- import { TrackedArray } from 'tracked-built-ins' ;
4
-
5
1
import AuthenticatedRoute from '../-authenticated-route' ;
6
2
7
- export default class TokenSettingsRoute extends AuthenticatedRoute {
8
- @service store ;
9
-
10
- async model ( ) {
11
- let apiTokens = await this . store . findAll ( 'api-token' ) ;
12
- return TrackedArray . from ( apiTokens . slice ( ) ) ;
13
- }
14
-
15
- /**
16
- * Ensure that all plaintext tokens are deleted from memory after leaving
17
- * the API tokens settings page.
18
- */
19
- resetController ( controller ) {
20
- for ( let token of controller . model ) {
21
- if ( token . token ) {
22
- token . token = undefined ;
23
- }
24
- }
25
- }
26
- }
3
+ export default class TokenSettingsRoute extends AuthenticatedRoute { }
Original file line number Diff line number Diff line change
1
+ import Route from '@ember/routing/route' ;
2
+ import { inject as service } from '@ember/service' ;
3
+
4
+ import { TrackedArray } from 'tracked-built-ins' ;
5
+
6
+ export default class TokenListRoute extends Route {
7
+ @service store ;
8
+
9
+ async model ( ) {
10
+ let apiTokens = await this . store . findAll ( 'api-token' ) ;
11
+ return TrackedArray . from ( apiTokens . slice ( ) ) ;
12
+ }
13
+
14
+ /**
15
+ * Ensure that all plaintext tokens are deleted from memory after leaving
16
+ * the API tokens settings page.
17
+ */
18
+ resetController ( controller ) {
19
+ for ( let token of controller . model ) {
20
+ if ( token . token ) {
21
+ token . token = undefined ;
22
+ }
23
+ }
24
+ }
25
+ }
Original file line number Diff line number Diff line change
1
+ import Route from '@ember/routing/route' ;
2
+
3
+ export default class TokenListRoute extends Route {
4
+ resetController ( controller ) {
5
+ controller . saveTokenTask . cancelAll ( ) ;
6
+ }
7
+ }
Original file line number Diff line number Diff line change
1
+ .form-group , .buttons {
2
+ position : relative;
3
+ margin : var (--space-s ) 0 ;
4
+ }
5
+
6
+ .form-group {
7
+ label {
8
+ display : block;
9
+ margin-bottom : var (--space-3xs );
10
+ font-weight : 600 ;
11
+ }
12
+ }
13
+
14
+ .buttons {
15
+ display : flex;
16
+ gap : var (--space-2xs );
17
+ flex-wrap : wrap;
18
+ }
19
+
20
+ .name-input {
21
+ max-width : 440px ;
22
+ width : 100% ;
23
+ padding : var (--space-2xs );
24
+ border : 1px solid # ada796 ;
25
+ border-radius : var (--space-3xs );
26
+
27
+ & [aria-invalid = "true" ] {
28
+ background : # fff2f2 ;
29
+ border-color : red;
30
+ }
31
+ }
32
+
33
+ .generate-button {
34
+ composes : yellow-button small from '../../../styles/shared/buttons.module.css' ;
35
+ border-radius : 4px ;
36
+
37
+ .spinner {
38
+ margin-left : var (--space-2xs );
39
+ }
40
+ }
41
+
42
+ .cancel-button {
43
+ composes : tan-button small from '../../../styles/shared/buttons.module.css' ;
44
+ border-radius : 4px ;
45
+ }
Original file line number Diff line number Diff line change 33
33
background : linear-gradient (to bottom, var (--bg-color-top ) 0% , var (--bg-color-bottom ) 100% );
34
34
cursor : pointer;
35
35
36
+ & : hover , & : active , & : visited {
37
+ color : var (--text-color );
38
+ }
39
+
36
40
img , svg {
37
41
float : left;
38
42
display : inline-block;
Original file line number Diff line number Diff line change 3
3
<PageHeader @title =" Account Settings" />
4
4
5
5
<SettingsPage >
6
- < Settings::ApiTokens @ tokens = {{ @model }} />
6
+ {{ outlet }}
7
7
</SettingsPage >
Original file line number Diff line number Diff line change
1
+ <Settings::ApiTokens @tokens ={{ @model }} />
Original file line number Diff line number Diff line change
1
+ <h2 >New API Token</h2 >
2
+
3
+ <form local-class =" form" {{ on " submit" (prevent-default (perform this.saveTokenTask ))}} >
4
+ <div local-class =" form-group" >
5
+ <label for ={{ this.id }} >Name</label >
6
+ <Input
7
+ id ={{ this.id }}
8
+ @type =" text"
9
+ @value ={{ this.name }}
10
+ disabled ={{ this.saveTokenTask.isRunning }}
11
+ aria-required =" true"
12
+ aria-invalid ={{ if this.nameInvalid " true" " false" }}
13
+ local-class =" name-input"
14
+ data-test-name
15
+ {{ auto-focus }}
16
+ {{ on " input" this.resetNameValidation }}
17
+ />
18
+ </div >
19
+
20
+ <div local-class =" buttons" >
21
+ <button
22
+ type =" submit"
23
+ local-class =" generate-button"
24
+ disabled ={{ this.saveTokenTask.isRunning }}
25
+ data-test-generate
26
+ >
27
+ Generate Token
28
+
29
+ {{ #if this.saveTokenTask.isRunning }}
30
+ <LoadingSpinner local-class =" spinner" data-test-spinner />
31
+ {{ /if }}
32
+ </button >
33
+
34
+ <LinkTo
35
+ @route =" settings.tokens.index"
36
+ local-class =" cancel-button"
37
+ data-test-cancel
38
+ >
39
+ Cancel
40
+ </LinkTo >
41
+ </div >
42
+
43
+ </form >
You can’t perform that action at this time.
0 commit comments