|
1 | 1 | <script setup lang="ts"> |
2 | | -const { isConnected, isConnecting, npmUser, error, hasOperations, connect, disconnect } = |
3 | | - useConnector() |
| 2 | +const { |
| 3 | + isConnected, |
| 4 | + isConnecting, |
| 5 | + npmUser, |
| 6 | + error, |
| 7 | + hasOperations, |
| 8 | + operations, |
| 9 | + connect, |
| 10 | + disconnect, |
| 11 | + refreshState, |
| 12 | +} = useConnector() |
| 13 | +
|
| 14 | +const { settings } = useSettings() |
| 15 | +
|
| 16 | +const authUrl = computed(() => { |
| 17 | + const op = operations.value.find(o => o.status === 'running' && o.authUrl) |
| 18 | + return op?.authUrl ?? null |
| 19 | +}) |
| 20 | +
|
| 21 | +const AUTH_POLL_INTERVAL = 20_000 |
| 22 | +const AUTH_POLL_COUNT = 3 |
| 23 | +let authPollTimer: ReturnType<typeof setInterval> | null = null |
| 24 | +
|
| 25 | +function startAuthPolling() { |
| 26 | + stopAuthPolling() |
| 27 | + let remaining = AUTH_POLL_COUNT |
| 28 | + authPollTimer = setInterval(async () => { |
| 29 | + await refreshState() |
| 30 | + remaining-- |
| 31 | + if (remaining <= 0) { |
| 32 | + stopAuthPolling() |
| 33 | + } |
| 34 | + }, AUTH_POLL_INTERVAL) |
| 35 | +} |
| 36 | +
|
| 37 | +function stopAuthPolling() { |
| 38 | + if (authPollTimer) { |
| 39 | + clearInterval(authPollTimer) |
| 40 | + authPollTimer = null |
| 41 | + } |
| 42 | +} |
| 43 | +
|
| 44 | +onUnmounted(stopAuthPolling) |
| 45 | +
|
| 46 | +function handleOpenAuthUrl() { |
| 47 | + if (authUrl.value) { |
| 48 | + window.open(authUrl.value, '_blank', 'noopener,noreferrer') |
| 49 | + startAuthPolling() |
| 50 | + } |
| 51 | +} |
4 | 52 |
|
5 | 53 | const tokenInput = shallowRef('') |
6 | 54 | const portInput = shallowRef('31415') |
7 | 55 | const { copied, copy } = useClipboard({ copiedDuring: 2000 }) |
8 | 56 |
|
9 | 57 | const hasAttemptedConnect = shallowRef(false) |
10 | 58 |
|
| 59 | +watch( |
| 60 | + () => settings.value.connector.webAuth, |
| 61 | + webAuth => { |
| 62 | + if (!webAuth) { |
| 63 | + settings.value.connector.autoOpenURL = false |
| 64 | + } |
| 65 | + }, |
| 66 | +) |
| 67 | +
|
11 | 68 | watch(isConnected, connected => { |
12 | 69 | if (!connected) { |
13 | 70 | tokenInput.value = '' |
@@ -61,13 +118,40 @@ function handleDisconnect() { |
61 | 118 | </div> |
62 | 119 | </div> |
63 | 120 |
|
| 121 | + <!-- Connector preferences --> |
| 122 | + <div class="flex flex-col gap-2"> |
| 123 | + <SettingsToggle |
| 124 | + :label="$t('connector.modal.web_auth')" |
| 125 | + v-model="settings.connector.webAuth" |
| 126 | + /> |
| 127 | + <SettingsToggle |
| 128 | + :label="$t('connector.modal.auto_open_url')" |
| 129 | + v-model="settings.connector.autoOpenURL" |
| 130 | + :class="!settings.connector.webAuth ? 'opacity-50 pointer-events-none' : ''" |
| 131 | + :aria-disabled="!settings.connector.webAuth" |
| 132 | + /> |
| 133 | + </div> |
| 134 | + |
| 135 | + <div class="border-t border-border my-3" /> |
| 136 | + |
64 | 137 | <!-- Operations Queue --> |
65 | 138 | <OrgOperationsQueue /> |
66 | 139 |
|
67 | 140 | <div v-if="!hasOperations" class="text-sm text-fg-muted"> |
68 | 141 | {{ $t('connector.modal.connected_hint') }} |
69 | 142 | </div> |
70 | 143 |
|
| 144 | + <!-- Web auth link --> |
| 145 | + <button |
| 146 | + v-if="authUrl" |
| 147 | + type="button" |
| 148 | + class="flex items-center justify-center gap-2 w-full px-4 py-2 font-mono text-sm text-accent bg-accent/10 border border-accent/30 rounded-md transition-colors duration-200 hover:bg-accent/20 focus-visible:outline-accent/70" |
| 149 | + @click="handleOpenAuthUrl" |
| 150 | + > |
| 151 | + <span class="i-carbon:launch w-4 h-4" aria-hidden="true" /> |
| 152 | + {{ $t('operations.queue.open_web_auth') }} |
| 153 | + </button> |
| 154 | + |
71 | 155 | <button |
72 | 156 | type="button" |
73 | 157 | class="w-full px-4 py-2 font-mono text-sm text-fg-muted bg-bg-subtle border border-border rounded-md transition-colors duration-200 hover:text-fg hover:border-border-hover focus-visible:outline-accent/70" |
@@ -194,6 +278,20 @@ function handleDisconnect() { |
194 | 278 | class="w-full" |
195 | 279 | size="medium" |
196 | 280 | /> |
| 281 | + |
| 282 | + <div class="border-t border-border my-3" /> |
| 283 | + <div class="flex flex-col gap-2"> |
| 284 | + <SettingsToggle |
| 285 | + :label="$t('connector.modal.web_auth')" |
| 286 | + v-model="settings.connector.webAuth" |
| 287 | + /> |
| 288 | + <SettingsToggle |
| 289 | + :label="$t('connector.modal.auto_open_url')" |
| 290 | + v-model="settings.connector.autoOpenURL" |
| 291 | + :class="!settings.connector.webAuth ? 'opacity-50 pointer-events-none' : ''" |
| 292 | + :aria-disabled="!settings.connector.webAuth" |
| 293 | + /> |
| 294 | + </div> |
197 | 295 | </div> |
198 | 296 | </details> |
199 | 297 | </div> |
|
0 commit comments