diff options
Diffstat (limited to 'apps/settings/src/components/PersonalInfo/ProfileVisibilitySection')
2 files changed, 290 insertions, 0 deletions
diff --git a/apps/settings/src/components/PersonalInfo/ProfileVisibilitySection/ProfileVisibilitySection.vue b/apps/settings/src/components/PersonalInfo/ProfileVisibilitySection/ProfileVisibilitySection.vue new file mode 100644 index 00000000000..8acec883842 --- /dev/null +++ b/apps/settings/src/components/PersonalInfo/ProfileVisibilitySection/ProfileVisibilitySection.vue @@ -0,0 +1,128 @@ +<!-- + - SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> + +<template> + <!-- TODO remove this inline margin placeholder once the settings layout is updated --> + <section id="profile-visibility" + :style="{ marginLeft }"> + <HeaderBar :is-heading="true" :readable="heading" /> + + <em :class="{ disabled }"> + {{ t('settings', 'The more restrictive setting of either visibility or scope is respected on your Profile. For example, if visibility is set to "Show to everyone" and scope is set to "Private", "Private" is respected.') }} + </em> + + <div class="visibility-dropdowns" + :style="{ + gridTemplateRows: `repeat(${rows}, 44px)`, + }"> + <VisibilityDropdown v-for="param in visibilityParams" + :key="param.id" + :param-id="param.id" + :display-id="param.displayId" + :visibility.sync="param.visibility" /> + </div> + </section> +</template> + +<script> +import { loadState } from '@nextcloud/initial-state' +import { subscribe, unsubscribe } from '@nextcloud/event-bus' + +import HeaderBar from '../shared/HeaderBar.vue' +import VisibilityDropdown from './VisibilityDropdown.vue' +import { PROFILE_READABLE_ENUM } from '../../../constants/AccountPropertyConstants.js' + +const { profileConfig } = loadState('settings', 'profileParameters', {}) +const { profileEnabled } = loadState('settings', 'personalInfoParameters', false) + +const compareParams = (a, b) => { + if (a.appId === b.appId || (a.appId !== 'core' && b.appId !== 'core')) { + return a.displayId.localeCompare(b.displayId) + } else if (a.appId === 'core') { + return 1 + } else { + return -1 + } +} + +export default { + name: 'ProfileVisibilitySection', + + components: { + HeaderBar, + VisibilityDropdown, + }, + + data() { + return { + heading: PROFILE_READABLE_ENUM.PROFILE_VISIBILITY, + profileEnabled, + visibilityParams: Object.entries(profileConfig) + .map(([paramId, { appId, displayId, visibility }]) => ({ id: paramId, appId, displayId, visibility })) + .sort(compareParams), + // TODO remove this when not used once the settings layout is updated + marginLeft: window.matchMedia('(min-width: 1600px)').matches + ? window.getComputedStyle(document.getElementById('vue-avatar-section')).getPropertyValue('width').trim() + : '0px', + } + }, + + computed: { + disabled() { + return !this.profileEnabled + }, + + rows() { + return Math.ceil(this.visibilityParams.length / 2) + }, + }, + + mounted() { + subscribe('settings:profile-enabled:updated', this.handleProfileEnabledUpdate) + // TODO remove this when not used once the settings layout is updated + window.onresize = () => { + this.marginLeft = window.matchMedia('(min-width: 1600px)').matches + ? window.getComputedStyle(document.getElementById('vue-avatar-section')).getPropertyValue('width').trim() + : '0px' + } + }, + + beforeDestroy() { + unsubscribe('settings:profile-enabled:updated', this.handleProfileEnabledUpdate) + }, + + methods: { + handleProfileEnabledUpdate(profileEnabled) { + this.profileEnabled = profileEnabled + }, + }, +} +</script> + +<style lang="scss" scoped> +section { + padding: 30px; + max-width: 900px; + width: 100%; + + em { + display: block; + margin: 16px 0; + + &.disabled { + filter: grayscale(1); + opacity: 0.5; + cursor: default; + pointer-events: none; + + & *, + &:deep(*) { + cursor: default; + pointer-events: none; + } + } + } +} +</style> diff --git a/apps/settings/src/components/PersonalInfo/ProfileVisibilitySection/VisibilityDropdown.vue b/apps/settings/src/components/PersonalInfo/ProfileVisibilitySection/VisibilityDropdown.vue new file mode 100644 index 00000000000..aaa13e63e92 --- /dev/null +++ b/apps/settings/src/components/PersonalInfo/ProfileVisibilitySection/VisibilityDropdown.vue @@ -0,0 +1,162 @@ +<!-- + - SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> + +<template> + <div class="visibility-container" + :class="{ disabled }"> + <label :for="inputId"> + {{ displayId }} + </label> + <NcSelect :input-id="inputId" + class="visibility-container__select" + :clearable="false" + :options="visibilityOptions" + :value="visibilityObject" + label-outside + @option:selected="onVisibilityChange" /> + </div> +</template> + +<script> +import { loadState } from '@nextcloud/initial-state' +import { subscribe, unsubscribe } from '@nextcloud/event-bus' + +import NcSelect from '@nextcloud/vue/components/NcSelect' + +import { saveProfileParameterVisibility } from '../../../service/ProfileService.js' +import { VISIBILITY_PROPERTY_ENUM } from '../../../constants/ProfileConstants.js' +import { handleError } from '../../../utils/handlers.ts' + +const { profileEnabled } = loadState('settings', 'personalInfoParameters', false) + +export default { + name: 'VisibilityDropdown', + + components: { + NcSelect, + }, + + props: { + paramId: { + type: String, + required: true, + }, + displayId: { + type: String, + required: true, + }, + visibility: { + type: String, + required: true, + }, + }, + + data() { + return { + initialVisibility: this.visibility, + profileEnabled, + } + }, + + computed: { + disabled() { + return !this.profileEnabled + }, + + inputId() { + return `profile-visibility-${this.paramId}` + }, + + visibilityObject() { + return VISIBILITY_PROPERTY_ENUM[this.visibility] + }, + + visibilityOptions() { + return Object.values(VISIBILITY_PROPERTY_ENUM) + }, + }, + + mounted() { + subscribe('settings:profile-enabled:updated', this.handleProfileEnabledUpdate) + }, + + beforeDestroy() { + unsubscribe('settings:profile-enabled:updated', this.handleProfileEnabledUpdate) + }, + + methods: { + async onVisibilityChange(visibilityObject) { + // This check is needed as the argument is null when selecting the same option + if (visibilityObject !== null) { + const { name: visibility } = visibilityObject + this.$emit('update:visibility', visibility) + + if (visibility !== '') { + await this.updateVisibility(visibility) + } + } + }, + + async updateVisibility(visibility) { + try { + const responseData = await saveProfileParameterVisibility(this.paramId, visibility) + this.handleResponse({ + visibility, + status: responseData.ocs?.meta?.status, + }) + } catch (e) { + this.handleResponse({ + errorMessage: t('settings', 'Unable to update visibility of {displayId}', { displayId: this.displayId }), + error: e, + }) + } + }, + + handleResponse({ visibility, status, errorMessage, error }) { + if (status === 'ok') { + // Ensure that local state reflects server state + this.initialVisibility = visibility + } else { + handleError(error, errorMessage) + } + }, + + handleProfileEnabledUpdate(profileEnabled) { + this.profileEnabled = profileEnabled + }, + }, +} +</script> + +<style lang="scss" scoped> +.visibility-container { + display: flex; + flex-wrap: wrap; + + &.disabled { + filter: grayscale(1); + opacity: 0.5; + cursor: default; + pointer-events: none; + + & *, + &:deep(*) { + cursor: default; + pointer-events: none; + } + } + + label { + color: var(--color-text-lighter); + width: 150px; + line-height: 50px; + } + + &__select { + width: 270px; + max-width: 40vw; + } +} +</style> |