Skip to content

Commit b38d01e

Browse files
authored
set max-width to 300px for q panel + other fixes (#4397)
* set max-width to 300px for q panel + other fixes * back button show not show in authenticating state * fix a bug in the condition check * Disable continue button correctly and show back arrow correctly * fix user canceled condition check and some other UI fixes
1 parent 9caf3bf commit b38d01e

File tree

19 files changed

+141
-90
lines changed

19 files changed

+141
-90
lines changed

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/credentials/LoginUtils.kt

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package software.aws.toolkits.jetbrains.core.credentials
66
import com.intellij.openapi.progress.ProcessCanceledException
77
import com.intellij.openapi.project.Project
88
import com.intellij.openapi.vfs.VirtualFileManager
9-
import org.slf4j.LoggerFactory
109
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
1110
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
1211
import software.amazon.awssdk.profiles.Profile
@@ -18,7 +17,6 @@ import software.amazon.awssdk.services.ssooidc.model.SsoOidcException
1817
import software.amazon.awssdk.services.sts.StsClient
1918
import software.aws.toolkits.core.credentials.validatedSsoIdentifierFromUrl
2019
import software.aws.toolkits.core.region.AwsRegion
21-
import software.aws.toolkits.core.utils.error
2220
import software.aws.toolkits.core.utils.tryOrNull
2321
import software.aws.toolkits.jetbrains.core.AwsClientManager
2422
import software.aws.toolkits.jetbrains.core.credentials.profiles.SsoSessionConstants
@@ -30,15 +28,13 @@ import software.aws.toolkits.resources.message
3028
import software.aws.toolkits.telemetry.CredentialSourceId
3129
import java.io.IOException
3230

33-
private val LOG = LoggerFactory.getLogger("LoginUtils")
34-
3531
sealed interface Login {
3632
val id: CredentialSourceId
3733

3834
data class BuilderId(
3935
val scopes: List<String>,
4036
val onPendingToken: (InteractiveBearerTokenProvider) -> Unit,
41-
val onError: (String) -> Unit
37+
val onError: (Exception) -> Unit
4238
) : Login {
4339
override val id: CredentialSourceId = CredentialSourceId.AwsId
4440

@@ -53,7 +49,7 @@ sealed interface Login {
5349
val region: AwsRegion,
5450
val scopes: List<String>,
5551
val onPendingToken: (InteractiveBearerTokenProvider) -> Unit,
56-
val onError: (String) -> Unit
52+
val onError: (Exception, AuthProfile) -> Unit
5753
) : Login {
5854
override val id: CredentialSourceId = CredentialSourceId.IamIdentityCenter
5955
private val configFilesFacade = DefaultConfigFilesFacade()
@@ -156,7 +152,7 @@ fun authAndUpdateConfig(
156152
profile: UserConfigSsoSessionProfile,
157153
configFilesFacade: ConfigFilesFacade,
158154
onPendingToken: (InteractiveBearerTokenProvider) -> Unit,
159-
onError: (String) -> Unit
155+
onError: (Exception, AuthProfile) -> Unit
160156
): AwsBearerTokenConnection? {
161157
val requestedScopes = profile.scopes
162158
val allScopes = requestedScopes.toMutableSet()
@@ -181,10 +177,7 @@ fun authAndUpdateConfig(
181177
reauthConnectionIfNeeded(project, connection, onPendingToken)
182178
}
183179
} catch (e: Exception) {
184-
val message = ssoErrorMessageFromException(e)
185-
186-
onError(message)
187-
LOG.error(e) { "Failed to authenticate: message: $message; profile: $profile" }
180+
onError(e, profile)
188181
return null
189182
}
190183

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/credentials/ToolkitAuthManager.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ fun loginSso(
108108
region: String,
109109
requestedScopes: List<String>,
110110
onPendingToken: (InteractiveBearerTokenProvider) -> Unit = {},
111-
onError: (String) -> Unit = {}
111+
onError: (Exception) -> Unit = {}
112112
): AwsBearerTokenConnection? {
113113
fun createAndAuthNewConnection(profile: AuthProfile): AwsBearerTokenConnection? {
114114
val authManager = ToolkitAuthManager.getInstance()
@@ -117,9 +117,7 @@ fun loginSso(
117117
reauthConnectionIfNeeded(project, transientConnection, onPendingToken)
118118
}
119119
} catch (e: Exception) {
120-
val message = ssoErrorMessageFromException(e)
121-
122-
onError(message)
120+
onError(e)
123121
null
124122
}
125123

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/credentials/sso/SsoAccessTokenProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ class SsoAccessTokenProvider(
273273
} catch (e: ProcessCanceledException) {
274274
future.cancel(true)
275275
_authorization.set(null)
276-
throw ProcessCanceledException(IllegalStateException("Login canceled by user"))
276+
throw ProcessCanceledException(IllegalStateException(message("credentials.pending.user_cancel.message")))
277277
}
278278
}
279279
}

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/webview/LoginBrowser.kt

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package software.aws.toolkits.jetbrains.core.webview
55

66
import com.intellij.openapi.application.ApplicationManager
77
import com.intellij.openapi.application.runInEdt
8+
import com.intellij.openapi.progress.ProcessCanceledException
89
import com.intellij.openapi.progress.blockingContext
910
import com.intellij.openapi.project.Project
1011
import com.intellij.platform.ide.progress.withBackgroundProgress
@@ -14,13 +15,18 @@ import com.intellij.ui.jcef.JBCefJSQuery
1415
import kotlinx.coroutines.launch
1516
import kotlinx.coroutines.runBlocking
1617
import software.aws.toolkits.core.region.AwsRegion
18+
import software.aws.toolkits.core.utils.debug
19+
import software.aws.toolkits.core.utils.error
20+
import software.aws.toolkits.core.utils.getLogger
1721
import software.aws.toolkits.jetbrains.core.coroutines.projectCoroutineScope
22+
import software.aws.toolkits.jetbrains.core.credentials.AuthProfile
1823
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
1924
import software.aws.toolkits.jetbrains.core.credentials.Login
2025
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnection
2126
import software.aws.toolkits.jetbrains.core.credentials.reauthConnectionIfNeeded
2227
import software.aws.toolkits.jetbrains.core.credentials.sso.PendingAuthorization
2328
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.InteractiveBearerTokenProvider
29+
import software.aws.toolkits.jetbrains.core.credentials.ssoErrorMessageFromException
2430
import software.aws.toolkits.jetbrains.utils.pollFor
2531
import software.aws.toolkits.resources.message
2632
import software.aws.toolkits.telemetry.FeatureId
@@ -75,14 +81,22 @@ abstract class LoginBrowser(
7581
}
7682

7783
open fun loginBuilderId(scopes: List<String>) {
84+
val onError: (Exception) -> Unit = { e ->
85+
tryHandleUserCanceledLogin(e)
86+
// TODO: telemetry
87+
}
7888
loginWithBackgroundContext {
79-
Login.BuilderId(scopes, onPendingToken) {}.loginBuilderId(project)
89+
Login.BuilderId(scopes, onPendingToken, onError).loginBuilderId(project)
8090
// TODO: telemetry
8191
}
8292
}
8393

8494
open fun loginIdC(url: String, region: AwsRegion, scopes: List<String>) {
85-
val onError: (String) -> Unit = { _ ->
95+
val onError: (Exception, AuthProfile) -> Unit = { e, profile ->
96+
if (!tryHandleUserCanceledLogin(e)) {
97+
val message = ssoErrorMessageFromException(e)
98+
LOG.error(e) { "Failed to authenticate: message: $message; profile: $profile" }
99+
}
86100
// TODO: telemetry
87101
}
88102
loginWithBackgroundContext {
@@ -127,7 +141,19 @@ abstract class LoginBrowser(
127141
}
128142
}
129143

144+
private fun tryHandleUserCanceledLogin(e: Exception): Boolean {
145+
if (e !is ProcessCanceledException ||
146+
e.cause !is IllegalStateException ||
147+
e.message?.contains(message("credentials.pending.user_cancel.message")) == false
148+
) {
149+
return false
150+
}
151+
LOG.debug(e) { "User canceled login" }
152+
return true
153+
}
154+
130155
companion object {
156+
private val LOG = getLogger<LoginBrowser>()
131157
fun getWebviewHTML(webScriptUri: String, query: JBCefJSQuery): String {
132158
val colorMode = if (JBColor.isBright()) "jb-light" else "jb-dark"
133159
val postMessageToJavaJsCode = query.inject("JSON.stringify(message)")

plugins/core/webview/src/q-ui/assets/common.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ a {
3737
border-radius: 3px;
3838
border-width: 0;
3939
font-weight: bold;
40+
margin-bottom: 100px;
4041
}
4142

4243
.continue-button {
@@ -96,5 +97,9 @@ button:not([disabled]), .item-container {
9697
cursor: pointer;
9798
}
9899

100+
.centered-with-max-width {
101+
max-width: 300px;
102+
margin: 0 auto;
103+
}
99104

100105

plugins/core/webview/src/q-ui/components/awsProfileForm.vue

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,28 @@
22
<!-- SPDX-License-Identifier: Apache-2.0 -->
33

44
<template>
5-
<div class="title no-bold font-amazon">Profile Name</div>
6-
<div class="hint font-amazon">The identifier for these credentials</div>
7-
<input class="iamInput font-amazon" type="text" id="profileName" name="profileName" v-model="profileName"/>
5+
<div class="font-amazon" @keydown.enter="handleContinueClick">
6+
<div class="title no-bold">Profile Name</div>
7+
<div class="hint">The identifier for these credentials</div>
8+
<input class="iamInput font-amazon" type="text" id="profileName" name="profileName" v-model="profileName"/>
89

9-
<br/><br/>
10-
<div class="title no-bold font-amazon">Access Key</div>
11-
<input class="iamInput font-amazon" type="text" id="accessKey" name="accessKey" v-model="accessKey"/>
10+
<br/><br/>
11+
<div class="title no-bold">Access Key</div>
12+
<input class="iamInput font-amazon" type="text" id="accessKey" name="accessKey" v-model="accessKey"/>
1213

13-
<br/><br/>
14-
<div class="title no-bold font-amazon">Secret Key</div>
15-
<input class="iamInput font-amazon" type="text" id="secretKey" name="secretKey" v-model="secretKey"/>
14+
<br/><br/>
15+
<div class="title no-bold">Secret Key</div>
16+
<input class="iamInput font-amazon" type="text" id="secretKey" name="secretKey" v-model="secretKey"/>
1617

17-
<br/><br/>
18-
<button
19-
class="login-flow-button continue-button"
20-
:disabled="profileName.length <= 0 || accessKey.length <= 0 || secretKey.length <= 0"
21-
v-on:click="handleContinueClick()"
22-
>
23-
Continue
24-
</button>
18+
<br/><br/>
19+
<button
20+
class="login-flow-button continue-button"
21+
:disabled="profileName.length <= 0 || accessKey.length <= 0 || secretKey.length <= 0"
22+
v-on:click="handleContinueClick()"
23+
>
24+
Continue
25+
</button>
26+
</div>
2527
</template>
2628

2729
<script lang="ts">
@@ -41,6 +43,9 @@ export default defineComponent({
4143
async handleContinueClick() {
4244
this.$emit('login', new LongLivedIAM(this.profileName, this.accessKey, this.secretKey))
4345
},
46+
},
47+
mounted() {
48+
document.getElementById("profileName")?.focus()
4449
}
4550
})
4651
</script>

plugins/core/webview/src/q-ui/components/login.vue

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
<!-- This Vue File is the login webview of AWS Toolkit and Amazon Q.-->
55
<template>
6-
<div v-bind:class="[disabled ? 'disabled-form' : '']" class="auth-container">
7-
<button v-if="stage !== 'START' || cancellable" class="back-button" @click="handleBackButtonClick" tabindex="-1">
6+
<div v-bind:class="[disabled ? 'disabled-form' : '']" class="auth-container centered-with-max-width">
7+
<button v-if="(stage !== 'START' || cancellable) && stage !== 'AUTHENTICATING'" class="back-button" @click="handleBackButtonClick" tabindex="-1">
88
<svg width="24" height="24" viewBox="0 -3 13 16" fill="none" xmlns="http://www.w3.org/2000/svg">
99
<path
1010
d="M4.98667 0.0933332L5.73333 0.786666L1.57333 4.94667H12.0267V5.96H1.57333L5.73333 10.0667L4.98667 10.8133L0.0266666 5.8V5.10667L4.98667 0.0933332Z"
@@ -110,8 +110,4 @@ export default defineComponent({
110110
</script>
111111

112112
<style>
113-
.auth-container {
114-
margin-left: 20px;
115-
margin-right: 20px;
116-
}
117113
</style>

plugins/core/webview/src/q-ui/components/logo.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default defineComponent({
3131
</script>
3232

3333
<template>
34-
<div class="bottom-small-gap" :class="logoPosition">
34+
<div class="bottom-small-gap centered-with-max-width" :class="logoPosition">
3535
<!-- Icon -->
3636

3737
<svg

plugins/core/webview/src/q-ui/components/qOptions.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
></SelectableItem>
4040
<button
4141
class="login-flow-button continue-button font-amazon"
42-
:disabled="selectedLoginOption === 0"
42+
:disabled="selectedLoginOption === LoginIdentifier.NONE"
4343
v-on:click="handleContinueClick()"
4444
tabindex="-1"
4545
>
@@ -61,6 +61,9 @@ export default defineComponent({
6161
app: String
6262
},
6363
computed: {
64+
LoginIdentifier() {
65+
return LoginIdentifier
66+
},
6467
stage(): Stage {
6568
return this.$store.state.stage
6669
},

plugins/core/webview/src/q-ui/components/reauth.vue

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,35 @@
22
<!-- SPDX-License-Identifier: Apache-2.0 -->
33

44
<template>
5-
<button v-if="!this.authenticating && this.app === 'TOOLKIT'" class="back-button" @click="back" tabindex="-1">
6-
<svg width="24" height="24" viewBox="0 -3 13 16" fill="none" xmlns="http://www.w3.org/2000/svg">
7-
<path
8-
d="M4.98667 0.0933332L5.73333 0.786666L1.57333 4.94667H12.0267V5.96H1.57333L5.73333 10.0667L4.98667 10.8133L0.0266666 5.8V5.10667L4.98667 0.0933332Z"
9-
fill="#21A2FF"
10-
/>
11-
</svg>
12-
</button>
5+
<div class="centered-with-max-width">
6+
<button v-if="!this.authenticating && this.app === 'TOOLKIT'" class="back-button" @click="back" tabindex="-1">
7+
<svg width="24" height="24" viewBox="0 -3 13 16" fill="none" xmlns="http://www.w3.org/2000/svg">
8+
<path
9+
d="M4.98667 0.0933332L5.73333 0.786666L1.57333 4.94667H12.0267V5.96H1.57333L5.73333 10.0667L4.98667 10.8133L0.0266666 5.8V5.10667L4.98667 0.0933332Z"
10+
fill="#21A2FF"
11+
/>
12+
</svg>
13+
</button>
1314

14-
<Authenticating v-if="this.authenticating" :selected-login-option="this.reauthLoginOption" @cancel="onClickCancelReauth"/>
15-
<div v-else>
16-
<div class="font-amazon bottom-small-gap title centered">{{ title }}</div>
17-
<div class="font-amazon bottom-small-gap title centered">Please re-authenticate to continue</div>
15+
<Authenticating v-if="this.authenticating" :selected-login-option="this.reauthLoginOption" @cancel="onClickCancelReauth"/>
16+
<div v-else>
17+
<div class="font-amazon bottom-small-gap title centered">{{ title }}</div>
18+
<div class="font-amazon bottom-small-gap title centered">Please re-authenticate to continue</div>
1819

19-
<button
20-
class="login-flow-button continue-button font-amazon"
21-
v-on:click="reauth()"
22-
>
23-
Re-authenticate
24-
</button>
20+
<button
21+
class="login-flow-button continue-button font-amazon"
22+
v-on:click="reauth()"
23+
>
24+
Re-authenticate
25+
</button>
2526

26-
<button
27-
class="login-flow-button font-amazon logout"
28-
v-on:click="signout()"
29-
>
30-
Sign out
31-
</button>
27+
<button
28+
class="login-flow-button font-amazon logout"
29+
v-on:click="signout()"
30+
>
31+
Sign out
32+
</button>
33+
</div>
3234
</div>
3335
</template>
3436

0 commit comments

Comments
 (0)