aboutsummaryrefslogtreecommitdiffstats
path: root/apps/settings/src/components/PersonalInfo/ProfileSection
diff options
context:
space:
mode:
Diffstat (limited to 'apps/settings/src/components/PersonalInfo/ProfileSection')
-rw-r--r--apps/settings/src/components/PersonalInfo/ProfileSection/EditProfileAnchorLink.vue83
-rw-r--r--apps/settings/src/components/PersonalInfo/ProfileSection/ProfileCheckbox.vue73
-rw-r--r--apps/settings/src/components/PersonalInfo/ProfileSection/ProfilePreviewCard.vue173
-rw-r--r--apps/settings/src/components/PersonalInfo/ProfileSection/ProfileSection.vue89
4 files changed, 418 insertions, 0 deletions
diff --git a/apps/settings/src/components/PersonalInfo/ProfileSection/EditProfileAnchorLink.vue b/apps/settings/src/components/PersonalInfo/ProfileSection/EditProfileAnchorLink.vue
new file mode 100644
index 00000000000..3deb5340751
--- /dev/null
+++ b/apps/settings/src/components/PersonalInfo/ProfileSection/EditProfileAnchorLink.vue
@@ -0,0 +1,83 @@
+<!--
+ - SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+
+<template>
+ <a :class="{ disabled }"
+ href="#profile-visibility"
+ v-on="$listeners">
+ <ChevronDownIcon class="anchor-icon"
+ :size="22" />
+ {{ t('settings', 'Edit your Profile visibility') }}
+ </a>
+</template>
+
+<script>
+import ChevronDownIcon from 'vue-material-design-icons/ChevronDown.vue'
+
+export default {
+ name: 'EditProfileAnchorLink',
+
+ components: {
+ ChevronDownIcon,
+ },
+
+ props: {
+ profileEnabled: {
+ type: Boolean,
+ required: true,
+ },
+ },
+
+ computed: {
+ disabled() {
+ return !this.profileEnabled
+ },
+ },
+}
+</script>
+
+<style lang="scss">
+html {
+ scroll-behavior: smooth;
+
+ @media screen and (prefers-reduced-motion: reduce) {
+ scroll-behavior: auto;
+ }
+}
+</style>
+
+<style lang="scss" scoped>
+a {
+ display: block;
+ height: 44px;
+ width: min(100%, 290px);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ line-height: 44px;
+ padding: 0 16px;
+ margin: 14px auto;
+ border-radius: var(--border-radius-pill);
+ color: var(--color-text-maxcontrast);
+ background-color: transparent;
+
+ .anchor-icon {
+ display: inline-block;
+ vertical-align: middle;
+ margin-top: 6px;
+ margin-inline-end: 8px;
+ }
+
+ &:hover,
+ &:focus,
+ &:active {
+ color: var(--color-main-text);
+ background-color: var(--color-background-dark);
+ }
+
+ &.disabled {
+ pointer-events: none;
+ }
+}
+</style>
diff --git a/apps/settings/src/components/PersonalInfo/ProfileSection/ProfileCheckbox.vue b/apps/settings/src/components/PersonalInfo/ProfileSection/ProfileCheckbox.vue
new file mode 100644
index 00000000000..6eb7cf8c34c
--- /dev/null
+++ b/apps/settings/src/components/PersonalInfo/ProfileSection/ProfileCheckbox.vue
@@ -0,0 +1,73 @@
+<!--
+ - SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+
+<template>
+ <div class="checkbox-container">
+ <NcCheckboxRadioSwitch type="switch"
+ :checked.sync="isProfileEnabled"
+ :loading="loading"
+ @update:checked="saveEnableProfile">
+ {{ t('settings', 'Enable profile') }}
+ </NcCheckboxRadioSwitch>
+ </div>
+</template>
+
+<script>
+import { emit } from '@nextcloud/event-bus'
+
+import { savePrimaryAccountProperty } from '../../../service/PersonalInfo/PersonalInfoService.js'
+import { ACCOUNT_PROPERTY_ENUM } from '../../../constants/AccountPropertyConstants.js'
+import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
+import { handleError } from '../../../utils/handlers.ts'
+
+export default {
+ name: 'ProfileCheckbox',
+
+ components: {
+ NcCheckboxRadioSwitch,
+ },
+
+ props: {
+ profileEnabled: {
+ type: Boolean,
+ required: true,
+ },
+ },
+
+ data() {
+ return {
+ isProfileEnabled: this.profileEnabled,
+ loading: false,
+ }
+ },
+
+ methods: {
+ async saveEnableProfile() {
+ this.loading = true
+ try {
+ const responseData = await savePrimaryAccountProperty(ACCOUNT_PROPERTY_ENUM.PROFILE_ENABLED, this.isProfileEnabled)
+ this.handleResponse({
+ isProfileEnabled: this.isProfileEnabled,
+ status: responseData.ocs?.meta?.status,
+ })
+ } catch (e) {
+ this.handleResponse({
+ errorMessage: t('settings', 'Unable to update profile enabled state'),
+ error: e,
+ })
+ }
+ },
+
+ handleResponse({ isProfileEnabled, status, errorMessage, error }) {
+ if (status === 'ok') {
+ emit('settings:profile-enabled:updated', isProfileEnabled)
+ } else {
+ handleError(error, errorMessage)
+ }
+ this.loading = false
+ },
+ },
+}
+</script>
diff --git a/apps/settings/src/components/PersonalInfo/ProfileSection/ProfilePreviewCard.vue b/apps/settings/src/components/PersonalInfo/ProfileSection/ProfilePreviewCard.vue
new file mode 100644
index 00000000000..47894f64f34
--- /dev/null
+++ b/apps/settings/src/components/PersonalInfo/ProfileSection/ProfilePreviewCard.vue
@@ -0,0 +1,173 @@
+<!--
+ - SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+
+<template>
+ <a class="preview-card"
+ :class="{ disabled }"
+ :href="profilePageLink">
+ <NcAvatar class="preview-card__avatar"
+ :user="userId"
+ :size="48"
+ :show-user-status="true"
+ :show-user-status-compact="false"
+ :disable-menu="true"
+ :disable-tooltip="true" />
+ <div class="preview-card__header">
+ <span>{{ displayName }}</span>
+ </div>
+ <div class="preview-card__footer">
+ <span>{{ organisation }}</span>
+ </div>
+ </a>
+</template>
+
+<script>
+import { getCurrentUser } from '@nextcloud/auth'
+import { generateUrl } from '@nextcloud/router'
+
+import NcAvatar from '@nextcloud/vue/components/NcAvatar'
+
+export default {
+ name: 'ProfilePreviewCard',
+
+ components: {
+ NcAvatar,
+ },
+
+ props: {
+ displayName: {
+ type: String,
+ required: true,
+ },
+ organisation: {
+ type: String,
+ required: true,
+ },
+ profileEnabled: {
+ type: Boolean,
+ required: true,
+ },
+ userId: {
+ type: String,
+ required: true,
+ },
+ },
+
+ computed: {
+ disabled() {
+ return !this.profileEnabled
+ },
+
+ profilePageLink() {
+ if (this.profileEnabled) {
+ return generateUrl('/u/{userId}', { userId: getCurrentUser().uid })
+ }
+ // Since an anchor element is used rather than a button for better UX,
+ // this hack removes href if the profile is disabled so that disabling pointer-events is not needed to prevent a click from opening a page
+ // and to allow the hover event (which disabling pointer-events wouldn't allow) for styling
+ return null
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.preview-card {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ width: min(100%, 290px);
+ height: 116px;
+ margin: 14px auto;
+ border-radius: var(--border-radius-large);
+ background-color: var(--color-main-background);
+ font-weight: bold;
+ box-shadow: 0 2px 9px var(--color-box-shadow);
+
+ &:hover,
+ &:focus,
+ &:active {
+ box-shadow: 0 2px 12px var(--color-box-shadow);
+ }
+
+ &:focus-visible {
+ outline: var(--color-main-text) solid 1px;
+ outline-offset: 3px;
+ }
+
+ &.disabled {
+ filter: grayscale(1);
+ opacity: 0.5;
+ cursor: default;
+ box-shadow: 0 0 3px var(--color-box-shadow);
+
+ & *,
+ &:deep(*) {
+ cursor: default;
+ }
+ }
+
+ &__avatar {
+ // Override Avatar component position to fix positioning on rerender
+ position: absolute !important;
+ top: 40px;
+ inset-inline-start: 18px;
+ z-index: 1;
+
+ &:not(.avatardiv--unknown) {
+ box-shadow: 0 0 0 3px var(--color-main-background) !important;
+ }
+ }
+
+ &__header,
+ &__footer {
+ position: relative;
+ width: auto;
+
+ span {
+ position: absolute;
+ inset-inline-start: 78px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ overflow-wrap: anywhere;
+
+ @supports (-webkit-line-clamp: 2) {
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ }
+ }
+ }
+
+ &__header {
+ height: 70px;
+ border-radius: var(--border-radius-large) var(--border-radius-large) 0 0;
+ background-color: var(--color-primary-element);
+
+ span {
+ bottom: 0;
+ color: var(--color-primary-element-text);
+ font-size: 18px;
+ font-weight: bold;
+ margin-block: 0 8px;
+ margin-inline: 0 4px;
+ }
+ }
+
+ &__footer {
+ height: 46px;
+
+ span {
+ top: 0;
+ color: var(--color-text-maxcontrast);
+ font-size: 14px;
+ font-weight: normal;
+ margin-block: 4px 0;
+ margin-inline: 0 4px;
+ line-height: 1.3;
+ }
+ }
+}
+</style>
diff --git a/apps/settings/src/components/PersonalInfo/ProfileSection/ProfileSection.vue b/apps/settings/src/components/PersonalInfo/ProfileSection/ProfileSection.vue
new file mode 100644
index 00000000000..22c03f72697
--- /dev/null
+++ b/apps/settings/src/components/PersonalInfo/ProfileSection/ProfileSection.vue
@@ -0,0 +1,89 @@
+<!--
+ - SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+
+<template>
+ <section>
+ <HeaderBar :is-heading="true" :readable="propertyReadable" />
+
+ <ProfileCheckbox :profile-enabled.sync="profileEnabled" />
+
+ <ProfilePreviewCard :organisation="organisation"
+ :display-name="displayName"
+ :profile-enabled="profileEnabled"
+ :user-id="userId" />
+
+ <EditProfileAnchorLink :profile-enabled="profileEnabled" />
+ </section>
+</template>
+
+<script>
+import { loadState } from '@nextcloud/initial-state'
+import { subscribe, unsubscribe } from '@nextcloud/event-bus'
+
+import EditProfileAnchorLink from './EditProfileAnchorLink.vue'
+import HeaderBar from '../shared/HeaderBar.vue'
+import ProfileCheckbox from './ProfileCheckbox.vue'
+import ProfilePreviewCard from './ProfilePreviewCard.vue'
+
+import { ACCOUNT_PROPERTY_READABLE_ENUM } from '../../../constants/AccountPropertyConstants.js'
+
+const {
+ organisation: { value: organisation },
+ displayName: { value: displayName },
+ profileEnabled,
+ userId,
+} = loadState('settings', 'personalInfoParameters', {})
+
+export default {
+ name: 'ProfileSection',
+
+ components: {
+ EditProfileAnchorLink,
+ HeaderBar,
+ ProfileCheckbox,
+ ProfilePreviewCard,
+ },
+
+ data() {
+ return {
+ propertyReadable: ACCOUNT_PROPERTY_READABLE_ENUM.PROFILE_ENABLED,
+ organisation,
+ displayName,
+ profileEnabled,
+ userId,
+ }
+ },
+
+ mounted() {
+ subscribe('settings:display-name:updated', this.handleDisplayNameUpdate)
+ subscribe('settings:organisation:updated', this.handleOrganisationUpdate)
+ },
+
+ beforeDestroy() {
+ unsubscribe('settings:display-name:updated', this.handleDisplayNameUpdate)
+ unsubscribe('settings:organisation:updated', this.handleOrganisationUpdate)
+ },
+
+ methods: {
+ handleDisplayNameUpdate(displayName) {
+ this.displayName = displayName
+ },
+
+ handleOrganisationUpdate(organisation) {
+ this.organisation = organisation
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+section {
+ padding: 10px 10px;
+
+ &:deep(button:disabled) {
+ cursor: default;
+ }
+}
+</style>