aboutsummaryrefslogtreecommitdiffstats
path: root/apps/settings/src/components/AuthTokenSetupDialog.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/settings/src/components/AuthTokenSetupDialog.vue')
-rw-r--r--apps/settings/src/components/AuthTokenSetupDialog.vue203
1 files changed, 203 insertions, 0 deletions
diff --git a/apps/settings/src/components/AuthTokenSetupDialog.vue b/apps/settings/src/components/AuthTokenSetupDialog.vue
new file mode 100644
index 00000000000..3b8fac8dc1d
--- /dev/null
+++ b/apps/settings/src/components/AuthTokenSetupDialog.vue
@@ -0,0 +1,203 @@
+<!--
+ - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <NcDialog :open.sync="open"
+ :name="t('settings', 'New app password')"
+ content-classes="token-dialog">
+ <p>
+ {{ t('settings', 'Use the credentials below to configure your app or device. For security reasons this password will only be shown once.') }}
+ </p>
+ <div class="token-dialog__name">
+ <NcTextField :label="t('settings', 'Login')" :value="loginName" readonly />
+ <NcButton type="tertiary"
+ :title="copyLoginNameLabel"
+ :aria-label="copyLoginNameLabel"
+ @click="copyLoginName">
+ <template #icon>
+ <NcIconSvgWrapper :path="copyNameIcon" />
+ </template>
+ </NcButton>
+ </div>
+ <div class="token-dialog__password">
+ <NcTextField ref="appPassword"
+ :label="t('settings', 'Password')"
+ :value="appPassword"
+ readonly />
+ <NcButton type="tertiary"
+ :title="copyPasswordLabel"
+ :aria-label="copyPasswordLabel"
+ @click="copyPassword">
+ <template #icon>
+ <NcIconSvgWrapper :path="copyPasswordIcon" />
+ </template>
+ </NcButton>
+ </div>
+ <div class="token-dialog__qrcode">
+ <NcButton v-if="!showQRCode" @click="showQRCode = true">
+ {{ t('settings', 'Show QR code for mobile apps') }}
+ </NcButton>
+ <QR v-else :value="qrUrl" />
+ </div>
+ </NcDialog>
+</template>
+
+<script lang="ts">
+import type { ITokenResponse } from '../store/authtoken'
+
+import { mdiCheck, mdiContentCopy } from '@mdi/js'
+import { showError } from '@nextcloud/dialogs'
+import { translate as t } from '@nextcloud/l10n'
+import { getRootUrl } from '@nextcloud/router'
+import { defineComponent, type PropType } from 'vue'
+
+import QR from '@chenfengyuan/vue-qrcode'
+import NcButton from '@nextcloud/vue/components/NcButton'
+import NcDialog from '@nextcloud/vue/components/NcDialog'
+import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
+import NcTextField from '@nextcloud/vue/components/NcTextField'
+
+import logger from '../logger'
+
+export default defineComponent({
+ name: 'AuthTokenSetupDialog',
+ components: {
+ NcButton,
+ NcDialog,
+ NcIconSvgWrapper,
+ NcTextField,
+ QR,
+ },
+ props: {
+ token: {
+ type: Object as PropType<ITokenResponse|null>,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ isNameCopied: false,
+ isPasswordCopied: false,
+ showQRCode: false,
+ }
+ },
+ computed: {
+ open: {
+ get() {
+ return this.token !== null
+ },
+ set(value: boolean) {
+ if (!value) {
+ this.$emit('close')
+ }
+ },
+ },
+ copyPasswordIcon() {
+ return this.isPasswordCopied ? mdiCheck : mdiContentCopy
+ },
+ copyNameIcon() {
+ return this.isNameCopied ? mdiCheck : mdiContentCopy
+ },
+ appPassword() {
+ return this.token?.token ?? ''
+ },
+ loginName() {
+ return this.token?.loginName ?? ''
+ },
+ qrUrl() {
+ const server = window.location.protocol + '//' + window.location.host + getRootUrl()
+ return `nc://login/user:${this.loginName}&password:${this.appPassword}&server:${server}`
+ },
+ copyPasswordLabel() {
+ if (this.isPasswordCopied) {
+ return t('settings', 'App password copied!')
+ }
+ return t('settings', 'Copy app password')
+ },
+ copyLoginNameLabel() {
+ if (this.isNameCopied) {
+ return t('settings', 'Login name copied!')
+ }
+ return t('settings', 'Copy login name')
+ },
+ },
+ watch: {
+ token() {
+ // reset showing the QR code on token change
+ this.showQRCode = false
+ },
+ open() {
+ if (this.open) {
+ this.$nextTick(() => {
+ this.$refs.appPassword!.select()
+ })
+ }
+ },
+ },
+ methods: {
+ t,
+ async copyPassword() {
+ try {
+ await navigator.clipboard.writeText(this.appPassword)
+ this.isPasswordCopied = true
+ } catch (e) {
+ this.isPasswordCopied = false
+ logger.error(e as Error)
+ showError(t('settings', 'Could not copy app password. Please copy it manually.'))
+ } finally {
+ setTimeout(() => {
+ this.isPasswordCopied = false
+ }, 4000)
+ }
+ },
+ async copyLoginName() {
+ try {
+ await navigator.clipboard.writeText(this.loginName)
+ this.isNameCopied = true
+ } catch (e) {
+ this.isNameCopied = false
+ logger.error(e as Error)
+ showError(t('settings', 'Could not copy login name. Please copy it manually.'))
+ } finally {
+ setTimeout(() => {
+ this.isNameCopied = false
+ }, 4000)
+ }
+ },
+ },
+})
+</script>
+
+<style scoped lang="scss">
+:deep(.token-dialog) {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+
+ padding-inline: 22px;
+ padding-block-end: 20px;
+
+ > * {
+ box-sizing: border-box;
+ }
+}
+
+.token-dialog {
+ &__name, &__password {
+ align-items: end;
+ display: flex;
+ gap: 10px;
+
+ :deep(input) {
+ font-family: monospace;
+ }
+ }
+
+ &__qrcode {
+ display: flex;
+ justify-content: center;
+ }
+}
+</style>