Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ng <chrng8@gmail.com>2021-07-29 04:23:04 +0300
committerChristopher Ng <chrng8@gmail.com>2021-08-24 00:29:47 +0300
commita8ad5a3b6e40a076f97ac4847fe6acc2b9be4c8a (patch)
treeed547e21cd7c49f8a5ba8b24b8e531672372bb04 /apps/settings/src
parentdb182d6517f6a8086bd7e6bc6950f13f404adde2 (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.vue167
-rw-r--r--apps/settings/src/components/PersonalInfo/DisplayNameSection/DisplayNameSection.vue100
-rw-r--r--apps/settings/src/components/PersonalInfo/EmailSection/Email.vue105
-rw-r--r--apps/settings/src/components/PersonalInfo/EmailSection/EmailSection.vue24
-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.js25
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')