diff options
author | Christopher Ng <chrng8@gmail.com> | 2021-07-29 04:23:04 +0300 |
---|---|---|
committer | Christopher Ng <chrng8@gmail.com> | 2021-08-24 00:29:47 +0300 |
commit | a8ad5a3b6e40a076f97ac4847fe6acc2b9be4c8a (patch) | |
tree | ed547e21cd7c49f8a5ba8b24b8e531672372bb04 /apps/settings/src | |
parent | db182d6517f6a8086bd7e6bc6950f13f404adde2 (diff) |
Vuetify
- abstract shared components
- rewrite email section
Signed-off-by: Christopher Ng <chrng8@gmail.com>
Diffstat (limited to 'apps/settings/src')
-rw-r--r-- | apps/settings/src/components/PersonalInfo/DisplayNameSection/DisplayName.vue | 167 | ||||
-rw-r--r-- | apps/settings/src/components/PersonalInfo/DisplayNameSection/DisplayNameSection.vue | 100 | ||||
-rw-r--r-- | apps/settings/src/components/PersonalInfo/EmailSection/Email.vue | 105 | ||||
-rw-r--r-- | apps/settings/src/components/PersonalInfo/EmailSection/EmailSection.vue | 24 | ||||
-rw-r--r-- | apps/settings/src/components/PersonalInfo/shared/AddButton.vue (renamed from apps/settings/src/components/PersonalInfo/EmailSection/AddButton.vue) | 12 | ||||
-rw-r--r-- | apps/settings/src/components/PersonalInfo/shared/FederationControl.vue (renamed from apps/settings/src/components/PersonalInfo/EmailSection/FederationControl.vue) | 116 | ||||
-rw-r--r-- | apps/settings/src/components/PersonalInfo/shared/HeaderBar.vue (renamed from apps/settings/src/components/PersonalInfo/EmailSection/HeaderBar.vue) | 48 | ||||
-rw-r--r-- | apps/settings/src/main-personal-info.js | 25 |
8 files changed, 469 insertions, 128 deletions
diff --git a/apps/settings/src/components/PersonalInfo/DisplayNameSection/DisplayName.vue b/apps/settings/src/components/PersonalInfo/DisplayNameSection/DisplayName.vue new file mode 100644 index 00000000000..1f32f55dcf7 --- /dev/null +++ b/apps/settings/src/components/PersonalInfo/DisplayNameSection/DisplayName.vue @@ -0,0 +1,167 @@ +<!-- + - @copyright 2021, Christopher Ng <chrng8@gmail.com> + - + - @author Christopher Ng <chrng8@gmail.com> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. +--> + +<template> + <div class="displayname"> + <input + id="displayname" + ref="displayName" + type="text" + name="displayname" + :placeholder="t('settings', 'Your full name')" + :value="displayName" + autocapitalize="none" + autocomplete="on" + autocorrect="off" + required="true" + @input="onDisplayNameChange"> + + <div class="displayname__actions-container"> + <transition name="fade"> + <span v-if="showCheckmarkIcon" class="icon-checkmark" /> + <span v-else-if="showErrorIcon" class="icon-error" /> + </transition> + </div> + </div> +</template> + +<script> +import { showError } from '@nextcloud/dialogs' +import debounce from 'debounce' + +import { savePrimaryDisplayName } from '../../../service/PersonalInfo/DisplayNameService' + +// TODO Global avatar updating on events (e.g. updating the displayname) is currently being handled by global js, investigate using https://github.com/nextcloud/nextcloud-event-bus for global avatar updating + +export default { + name: 'DisplayName', + + props: { + displayName: { + type: String, + required: true, + }, + scope: { + type: String, + required: true, + }, + }, + + data() { + return { + initialDisplayName: this.displayName, + localScope: this.scope, + showCheckmarkIcon: false, + showErrorIcon: false, + } + }, + + methods: { + onDisplayNameChange(e) { + this.$emit('update:display-name', e.target.value.trim()) + // $nextTick() ensures that references to this.dipslayName further down the chain give the correct non-outdated value + this.$nextTick(() => this.debounceDisplayNameChange()) + }, + + debounceDisplayNameChange: debounce(async function() { + if (this.$refs.displayName?.checkValidity() && this.isValid()) { + await this.updatePrimaryDisplayName() + } + }, 500), + + async updatePrimaryDisplayName() { + try { + const responseData = await savePrimaryDisplayName(this.displayName) + this.handleResponse(responseData.ocs?.meta?.status) + } catch (e) { + this.handleResponse('error', 'Unable to update full name', e) + } + }, + + handleResponse(status, errorMessage, error) { + if (status === 'ok') { + // Ensure that local initialDiplayName state reflects server state + this.initialDisplayName = this.displayName + this.showCheckmarkIcon = true + setTimeout(() => { this.showCheckmarkIcon = false }, 2000) + } else { + showError(t('settings', errorMessage)) + this.logger.error(errorMessage, error) + this.showErrorIcon = true + setTimeout(() => { this.showErrorIcon = false }, 2000) + } + }, + + isValid() { + return this.displayName !== '' + }, + + onScopeChange(scope) { + this.$emit('update:scope', scope) + }, + }, +} +</script> + +<style lang="scss" scoped> + .displayname { + display: grid; + align-items: center; + + input[type=text] { + grid-area: 1 / 1; + } + + .displayname__actions-container { + grid-area: 1 / 1; + justify-self: flex-end; + height: 30px; + + display: flex; + gap: 0 2px; + margin-right: 5px; + + .icon-checkmark, + .icon-error { + height: 30px !important; + min-height: 30px !important; + width: 30px !important; + min-width: 30px !important; + top: 0; + right: 0; + float: none; + } + } + } + + .fade-enter, + .fade-leave-to { + opacity: 0; + } + + .fade-enter-active { + transition: opacity 200ms ease-out; + } + + .fade-leave-active { + transition: opacity 300ms ease-out; + } +</style> diff --git a/apps/settings/src/components/PersonalInfo/DisplayNameSection/DisplayNameSection.vue b/apps/settings/src/components/PersonalInfo/DisplayNameSection/DisplayNameSection.vue new file mode 100644 index 00000000000..100e7fad876 --- /dev/null +++ b/apps/settings/src/components/PersonalInfo/DisplayNameSection/DisplayNameSection.vue @@ -0,0 +1,100 @@ +<!-- + - @copyright 2021, Christopher Ng <chrng8@gmail.com> + - + - @author Christopher Ng <chrng8@gmail.com> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. +--> + +<template> + <form + ref="form" + class="section" + @submit.stop.prevent="() => {}"> + <HeaderBar + :account-property="accountProperty" + label-for="displayname" + :is-editable="displayNameChangeSupported" + :is-valid-form="isValidForm" + :handle-scope-change="savePrimaryDisplayNameScope" + :scope.sync="primaryDisplayName.scope" /> + + <template v-if="displayNameChangeSupported"> + <DisplayName + :scope.sync="primaryDisplayName.scope" + :display-name.sync="primaryDisplayName.value" + @update:display-name="onUpdateDisplayName" /> + </template> + + <span v-else> + {{ primaryDisplayName.value || t('settings', 'No full name set') }} + </span> + </form> +</template> + +<script> +import { loadState } from '@nextcloud/initial-state' + +import DisplayName from './DisplayName' +import HeaderBar from '../shared/HeaderBar' + +import { ACCOUNT_PROPERTY_READABLE_ENUM } from '../../../constants/AccountPropertyConstants' +import { savePrimaryDisplayNameScope } from '../../../service/PersonalInfo/DisplayNameService' + +const { displayNames: { primaryDisplayName } } = loadState('settings', 'personalInfoParameters', {}) +const { displayNameChangeSupported } = loadState('settings', 'accountParameters', {}) + +export default { + name: 'DisplayNameSection', + + components: { + DisplayName, + HeaderBar, + }, + + data() { + return { + accountProperty: ACCOUNT_PROPERTY_READABLE_ENUM.DISPLAYNAME, + displayNameChangeSupported, + isValidForm: true, + primaryDisplayName, + savePrimaryDisplayNameScope, + } + }, + + mounted() { + this.$nextTick(() => this.updateFormValidity()) + }, + + methods: { + onUpdateDisplayName() { + this.$nextTick(() => this.updateFormValidity()) + }, + + updateFormValidity() { + this.isValidForm = this.$refs.form?.checkValidity() + }, + }, +} +</script> + +<style lang="scss" scoped> + form::v-deep button { + &:disabled { + cursor: default; + } + } +</style> diff --git a/apps/settings/src/components/PersonalInfo/EmailSection/Email.vue b/apps/settings/src/components/PersonalInfo/EmailSection/Email.vue index faca83821e2..6c0a98d26f9 100644 --- a/apps/settings/src/components/PersonalInfo/EmailSection/Email.vue +++ b/apps/settings/src/components/PersonalInfo/EmailSection/Email.vue @@ -21,7 +21,7 @@ <template> <div> - <div class="email-container"> + <div class="email"> <input ref="email" type="email" @@ -34,21 +34,25 @@ required="true" @input="onEmailChange"> - <div class="email-actions-container"> + <div class="email__actions-container"> <transition name="fade"> <span v-if="showCheckmarkIcon" class="icon-checkmark" /> <span v-else-if="showErrorIcon" class="icon-error" /> </transition> - <FederationControl v-if="!primary" - class="federation-control" - :disabled="federationDisabled" - :email="email" - :scope.sync="localScope" - @update:scope="onScopeChange" /> + <template v-if="!primary"> + <FederationControl + :account-property="accountProperty" + :additional="true" + :additional-value="email" + :disabled="federationDisabled" + :handle-scope-change="saveAdditionalEmailScope" + :scope.sync="localScope" + @update:scope="onScopeChange" /> + </template> <Actions - class="actions-email" + class="email__actions" :aria-label="t('settings', 'Email options')" :disabled="deleteDisabled" :force-menu="true"> @@ -75,8 +79,10 @@ import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' import { showError } from '@nextcloud/dialogs' import debounce from 'debounce' -import FederationControl from './FederationControl' -import { savePrimaryEmail, saveAdditionalEmail, updateAdditionalEmail, removeAdditionalEmail } from '../../../service/PersonalInfoService' +import FederationControl from '../shared/FederationControl' + +import { ACCOUNT_PROPERTY_READABLE_ENUM } from '../../../constants/AccountPropertyConstants' +import { savePrimaryEmail, saveAdditionalEmail, saveAdditionalEmailScope, updateAdditionalEmail, removeAdditionalEmail } from '../../../service/PersonalInfo/EmailService' export default { name: 'Email', @@ -92,60 +98,62 @@ export default { type: String, required: true, }, - scope: { - type: String, - required: true, + index: { + type: Number, + default: 0, }, primary: { type: Boolean, default: false, }, - index: { - type: Number, - default: 0, + scope: { + type: String, + required: true, }, }, data() { return { + accountProperty: ACCOUNT_PROPERTY_READABLE_ENUM.EMAIL, initialEmail: this.email, localScope: this.scope, + saveAdditionalEmailScope, showCheckmarkIcon: false, showErrorIcon: false, } }, computed: { - inputName() { + deleteDisabled() { if (this.primary) { - return 'email' + return this.email === '' } - return 'additionalEmail[]' + return this.email !== '' && !this.isValid() }, - inputPlaceholder() { + deleteEmailLabel() { if (this.primary) { - return t('settings', 'Your email address') + return t('settings', 'Remove primary email') } - return t('settings', 'Additional email address {index}', { index: this.index + 1 }) + return t('settings', 'Delete email') }, federationDisabled() { return !this.initialEmail }, - deleteDisabled() { + inputName() { if (this.primary) { - return this.email === '' + return 'email' } - return this.email !== '' && !this.isValid() + return 'additionalEmail[]' }, - deleteEmailLabel() { + inputPlaceholder() { if (this.primary) { - return t('settings', 'Remove primary email') + return t('settings', 'Your email address') } - return t('settings', 'Delete email') + return t('settings', 'Additional email address {index}', { index: this.index + 1 }) }, }, @@ -157,7 +165,7 @@ export default { methods: { onEmailChange(e) { - this.$emit('update:email', e.target.value) + this.$emit('update:email', e.target.value.trim()) // $nextTick() ensures that references to this.email further down the chain give the correct non-outdated value this.$nextTick(() => this.debounceEmailChange()) }, @@ -227,13 +235,9 @@ export default { } }, - isValid() { - return /^\S+$/.test(this.email) - }, - handleDeleteAdditionalEmail(status) { if (status === 'ok') { - this.$emit('deleteAdditionalEmail') + this.$emit('delete-additional-email') } else { this.handleResponse('error', 'Unable to delete additional email address', {}) } @@ -253,6 +257,10 @@ export default { } }, + isValid() { + return /^\S+$/.test(this.email) + }, + onScopeChange(scope) { this.$emit('update:scope', scope) }, @@ -261,7 +269,7 @@ export default { </script> <style lang="scss" scoped> - .email-container { + .email { display: grid; align-items: center; @@ -269,7 +277,7 @@ export default { grid-area: 1 / 1; } - .email-actions-container { + .email__actions-container { grid-area: 1 / 1; justify-self: flex-end; height: 30px; @@ -278,7 +286,7 @@ export default { gap: 0 2px; margin-right: 5px; - .actions-email { + .email__actions { opacity: 0.4 !important; &:hover { @@ -293,17 +301,6 @@ export default { } } - .federation-control { - &::v-deep button { - // TODO remove this hack - padding-bottom: 7px; - height: 30px !important; - min-height: 30px !important; - width: 30px !important; - min-width: 30px !important; - } - } - .icon-checkmark, .icon-error { height: 30px !important; @@ -317,6 +314,11 @@ export default { } } + .fade-enter, + .fade-leave-to { + opacity: 0; + } + .fade-enter-active { transition: opacity 200ms ease-out; } @@ -324,9 +326,4 @@ export default { .fade-leave-active { transition: opacity 300ms ease-out; } - - .fade-enter, - .fade-leave-to { - opacity: 0; - } </style> diff --git a/apps/settings/src/components/PersonalInfo/EmailSection/EmailSection.vue b/apps/settings/src/components/PersonalInfo/EmailSection/EmailSection.vue index 57e9ac60357..7b4a7b8f4eb 100644 --- a/apps/settings/src/components/PersonalInfo/EmailSection/EmailSection.vue +++ b/apps/settings/src/components/PersonalInfo/EmailSection/EmailSection.vue @@ -25,10 +25,14 @@ class="section" @submit.stop.prevent="() => {}"> <HeaderBar - :can-edit-emails="displayNameChangeSupported" + :account-property="accountProperty" + label-for="email" + :handle-scope-change="savePrimaryEmailScope" + :is-editable="displayNameChangeSupported" + :is-multi-value-supported="true" :is-valid-form="isValidForm" :scope.sync="primaryEmail.scope" - @addAdditionalEmail="onAddAdditionalEmail" /> + @add-additional="onAddAdditionalEmail" /> <template v-if="displayNameChangeSupported"> <Email @@ -42,7 +46,7 @@ :scope.sync="additionalEmail.scope" :email.sync="additionalEmail.value" @update:email="onUpdateEmail" - @deleteAdditionalEmail="onDeleteAdditionalEmail(index)" /> + @delete-additional-email="onDeleteAdditionalEmail(index)" /> </template> <span v-else> @@ -54,14 +58,14 @@ <script> import { loadState } from '@nextcloud/initial-state' import { showError } from '@nextcloud/dialogs' -import '@nextcloud/dialogs/styles/toast.scss' -import HeaderBar from './HeaderBar' import Email from './Email' -import { savePrimaryEmail, removeAdditionalEmail } from '../../../service/PersonalInfoService' -import { DEFAULT_ADDITIONAL_EMAIL_SCOPE } from '../../../constants/AccountPropertyConstants' +import HeaderBar from '../shared/HeaderBar' -const { additionalEmails, primaryEmail } = loadState('settings', 'emails', {}) +import { ACCOUNT_PROPERTY_READABLE_ENUM, DEFAULT_ADDITIONAL_EMAIL_SCOPE } from '../../../constants/AccountPropertyConstants' +import { savePrimaryEmail, savePrimaryEmailScope, removeAdditionalEmail } from '../../../service/PersonalInfo/EmailService' + +const { emails: { additionalEmails, primaryEmail } } = loadState('settings', 'personalInfoParameters', {}) const { displayNameChangeSupported } = loadState('settings', 'accountParameters', {}) export default { @@ -74,10 +78,12 @@ export default { data() { return { + accountProperty: ACCOUNT_PROPERTY_READABLE_ENUM.EMAIL, additionalEmails, displayNameChangeSupported, - primaryEmail, isValidForm: true, + primaryEmail, + savePrimaryEmailScope, } }, diff --git a/apps/settings/src/components/PersonalInfo/EmailSection/AddButton.vue b/apps/settings/src/components/PersonalInfo/shared/AddButton.vue index 83a293ed234..32605c3fdcf 100644 --- a/apps/settings/src/components/PersonalInfo/EmailSection/AddButton.vue +++ b/apps/settings/src/components/PersonalInfo/shared/AddButton.vue @@ -54,8 +54,8 @@ export default { border: none; background-color: transparent; - &:hover { - background-color: rgba(127, 127, 127, .15); + .icon { + margin-right: 8px; } &:enabled { @@ -66,13 +66,13 @@ export default { } } + &:hover { + background-color: rgba(127, 127, 127, .15); + } + &:enabled:hover { background-color: rgba(127, 127, 127, .25); opacity: 0.8 !important; } - - .icon { - margin-right: 8px; - } } </style> diff --git a/apps/settings/src/components/PersonalInfo/EmailSection/FederationControl.vue b/apps/settings/src/components/PersonalInfo/shared/FederationControl.vue index 6d48c157d6b..550f1710ab1 100644 --- a/apps/settings/src/components/PersonalInfo/EmailSection/FederationControl.vue +++ b/apps/settings/src/components/PersonalInfo/shared/FederationControl.vue @@ -21,15 +21,15 @@ <template> <Actions - class="actions-federation" - :aria-label="t('settings', 'Change privacy level of email')" + :class="{ 'federation-actions': !additional, 'federation-actions--additional': additional }" + :aria-label="ariaLabel" :default-icon="scopeIcon" :disabled="disabled"> <ActionButton v-for="federationScope in federationScopes" :key="federationScope.name" - class="forced-action" - :class="{ 'forced-active': scope === federationScope.name }" :aria-label="federationScope.tooltip" + class="federation-actions__btn" + :class="{ 'federation-actions__btn--active': scope === federationScope.name }" :close-after-click="true" :icon="federationScope.iconClass" :title="federationScope.displayName" @@ -45,14 +45,10 @@ import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' import { loadState } from '@nextcloud/initial-state' import { showError } from '@nextcloud/dialogs' -import { SCOPE_ENUM, SCOPE_PROPERTY_ENUM } from '../../../constants/AccountPropertyConstants' -import { savePrimaryEmailScope, saveAdditionalEmailScope } from '../../../service/PersonalInfoService' +import { ACCOUNT_PROPERTY_READABLE_ENUM, PROPERTY_READABLE_SUPPORTED_SCOPES_ENUM, SCOPE_ENUM, SCOPE_PROPERTY_ENUM } from '../../../constants/AccountPropertyConstants' const { lookupServerUploadEnabled } = loadState('settings', 'accountParameters', {}) -// TODO hardcoded for email, should abstract this for other sections -const excludedScopes = [SCOPE_ENUM.PRIVATE] - export default { name: 'FederationControl', @@ -62,49 +58,63 @@ export default { }, props: { - primary: { + accountProperty: { + type: String, + required: true, + validator: (value) => Object.values(ACCOUNT_PROPERTY_READABLE_ENUM).includes(value), + }, + additional: { type: Boolean, default: false, }, - email: { + additionalValue: { type: String, default: '', }, - scope: { - type: String, - required: true, - }, disabled: { type: Boolean, default: false, }, + handleScopeChange: { + type: Function, + required: true, + }, + scope: { + type: String, + required: true, + }, }, data() { return { + accountPropertyLowerCase: this.accountProperty.toLowerCase(), initialScope: this.scope, } }, computed: { + ariaLabel() { + return t('settings', 'Change privacy level of {accountProperty}', { accountProperty: this.accountPropertyLowerCase }) + }, + federationScopes() { - return Object.values(SCOPE_PROPERTY_ENUM).filter(({ name }) => !this.unsupportedScopes.includes(name)) + return Object.values(SCOPE_PROPERTY_ENUM).filter(({ name }) => this.supportedScopes.includes(name)) }, - unsupportedScopes() { - if (!lookupServerUploadEnabled) { + scopeIcon() { + return SCOPE_PROPERTY_ENUM[this.scope].iconClass + }, + + supportedScopes() { + if (lookupServerUploadEnabled) { return [ - ...excludedScopes, + ...PROPERTY_READABLE_SUPPORTED_SCOPES_ENUM[this.accountProperty], SCOPE_ENUM.FEDERATED, SCOPE_ENUM.PUBLISHED, ] } - return excludedScopes - }, - - scopeIcon() { - return SCOPE_PROPERTY_ENUM[this.scope].iconClass + return PROPERTY_READABLE_SUPPORTED_SCOPES_ENUM[this.accountProperty] }, }, @@ -113,29 +123,45 @@ export default { this.$emit('update:scope', scope) this.$nextTick(async() => { - if (this.primary) { - await this.updatePrimaryEmailScope() + if (!this.additional) { + await this.updatePrimaryScope() } else { - await this.updateAdditionalEmailScope() + await this.updateAdditionalScope() } }) }, - async updatePrimaryEmailScope() { + async updatePrimaryScope() { try { - const responseData = await savePrimaryEmailScope(this.scope) + const responseData = await this.handleScopeChange(this.scope) this.handleResponse(responseData.ocs?.meta?.status) } catch (e) { - this.handleResponse('error', 'Unable to update federation scope of the primary email', e) + this.handleResponse( + 'error', + t( + 'settings', + 'Unable to update federation scope of the primary {accountProperty}', + { accountProperty: this.accountPropertyLowerCase } + ), + e, + ) } }, - async updateAdditionalEmailScope() { + async updateAdditionalScope() { try { - const responseData = await saveAdditionalEmailScope(this.email, this.scope) + const responseData = await this.handleScopeChange(this.additionalValue, this.scope) this.handleResponse(responseData.ocs?.meta?.status) } catch (e) { - this.handleResponse('error', 'Unable to update federation scope of additional email', e) + this.handleResponse( + 'error', + t( + 'settings', + 'Unable to update federation scope of additional {accountProperty}', + { accountProperty: this.accountPropertyLowerCase } + ), + e, + ) } }, @@ -144,7 +170,7 @@ export default { this.initialScope = this.scope } else { this.$emit('update:scope', this.initialScope) - showError(t('settings', errorMessage)) + showError(errorMessage) this.logger.error(errorMessage, error) } }, @@ -153,7 +179,8 @@ export default { </script> <style lang="scss" scoped> - .actions-federation { + .federation-actions, + .federation-actions--additional { opacity: 0.4 !important; &:hover { @@ -161,12 +188,18 @@ export default { } } - .forced-active { - background-color: var(--color-primary-light) !important; - box-shadow: inset 2px 0 var(--color-primary) !important; + .federation-actions--additional { + &::v-deep button { + // TODO remove this hack + padding-bottom: 7px; + height: 30px !important; + min-height: 30px !important; + width: 30px !important; + min-width: 30px !important; + } } - .forced-action { + .federation-actions__btn { &::v-deep p { width: 150px !important; padding: 8px 0 !important; @@ -175,4 +208,9 @@ export default { line-height: 1.5em !important; } } + + .federation-actions__btn--active { + background-color: var(--color-primary-light) !important; + box-shadow: inset 2px 0 var(--color-primary) !important; + } </style> diff --git a/apps/settings/src/components/PersonalInfo/EmailSection/HeaderBar.vue b/apps/settings/src/components/PersonalInfo/shared/HeaderBar.vue index 7d2b1ab76b6..dcbbc2d09d7 100644 --- a/apps/settings/src/components/PersonalInfo/EmailSection/HeaderBar.vue +++ b/apps/settings/src/components/PersonalInfo/shared/HeaderBar.vue @@ -21,44 +21,66 @@ <template> <h3> - <label for="email"> - {{ t('settings', 'Email') }} + <label :for="labelFor"> + {{ t('settings', accountProperty) }} </label> <FederationControl class="federation-control" - :primary="true" + :account-property="accountProperty" + :handle-scope-change="handleScopeChange" :scope.sync="localScope" @update:scope="onScopeChange" /> - <AddButton v-if="canEditEmails" - class="add-button" - :disabled="!isValidForm" - @click.stop.prevent="addAdditionalEmail" /> + <template v-if="isEditable && isMultiValueSupported"> + <AddButton + class="add-button" + :disabled="!isValidForm" + @click.stop.prevent="onAddAdditional" /> + </template> </h3> </template> <script> -import FederationControl from './FederationControl' import AddButton from './AddButton' +import FederationControl from './FederationControl' + +import { ACCOUNT_PROPERTY_READABLE_ENUM } from '../../../constants/AccountPropertyConstants' export default { name: 'HeaderBar', components: { - FederationControl, AddButton, + FederationControl, }, props: { - canEditEmails: { + accountProperty: { + type: String, + required: true, + validator: (value) => Object.values(ACCOUNT_PROPERTY_READABLE_ENUM).includes(value), + }, + handleScopeChange: { + type: Function, + required: true, + }, + isEditable: { type: Boolean, - default: true, + required: true, + }, + isMultiValueSupported: { + type: Boolean, + default: false, }, isValidForm: { type: Boolean, default: true, }, + labelFor: { + type: String, + required: true, + }, scope: { type: String, required: true, @@ -72,8 +94,8 @@ export default { }, methods: { - addAdditionalEmail() { - this.$emit('addAdditionalEmail') + onAddAdditional() { + this.$emit('add-additional') }, onScopeChange(scope) { diff --git a/apps/settings/src/main-personal-info.js b/apps/settings/src/main-personal-info.js index 8edbd29669f..72ed4c15230 100644 --- a/apps/settings/src/main-personal-info.js +++ b/apps/settings/src/main-personal-info.js @@ -21,18 +21,29 @@ */ import Vue from 'vue' +import { getRequestToken } from '@nextcloud/auth' +import { translate as t } from '@nextcloud/l10n' +import '@nextcloud/dialogs/styles/toast.scss' import logger from './logger' +import DisplayNameSection from './components/PersonalInfo/DisplayNameSection/DisplayNameSection' import EmailSection from './components/PersonalInfo/EmailSection/EmailSection' // eslint-disable-next-line camelcase -__webpack_nonce__ = btoa(OC.requestToken) +__webpack_nonce__ = btoa(getRequestToken()) -Vue.prototype.t = t -Vue.prototype.logger = logger - -const View = Vue.extend(EmailSection) -export default new View({ - el: '#vue-emailsection', +Vue.mixin({ + props: { + logger, + }, + methods: { + t, + }, }) + +const DisplayNameView = Vue.extend(DisplayNameSection) +const EmailView = Vue.extend(EmailSection) + +new DisplayNameView().$mount('#vue-displaynamesection') +new EmailView().$mount('#vue-emailsection') |