Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable notification emails #8911

Merged
merged 20 commits into from
Apr 27, 2023
248 changes: 175 additions & 73 deletions packages/web-runtime/src/pages/account.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<main id="account" class="oc-height-1-1 oc-m">
<app-loading-spinner v-if="isLoading" />
<main v-else id="account" class="oc-height-1-1 oc-m">
<div class="oc-flex oc-flex-between oc-flex-bottom oc-width-1-1 oc-border-b oc-py">
<h1 id="account-page-title" class="oc-page-title oc-m-rm">{{ pageTitle }}</h1>
<div>
Expand Down Expand Up @@ -68,12 +69,15 @@
/>
</dd>
</div>
<div v-if="isLanguageSupported" class="account-page-info-language oc-mb oc-width-1-2@s">
<div
v-if="isSettingsServiceSupported"
class="account-page-info-language oc-mb oc-width-1-2@s"
>
<dt class="oc-text-normal oc-text-muted" v-text="$gettext('Language')" />
<dd data-testid="language">
<oc-select
v-if="languageOptions"
:model-value="selectedLanguageOption"
:model-value="selectedLanguageValue"
:clearable="false"
:options="languageOptions"
@update:model-value="updateSelectedLanguage"
Expand All @@ -100,45 +104,60 @@
<gdpr-export />
</dd>
</div>
<div class="account-page-notification oc-mb oc-width-1-2@s">
AlexAndBear marked this conversation as resolved.
Show resolved Hide resolved
<dt class="oc-text-normal oc-text-muted" v-text="$gettext('Notifications')" />
<dd data-testid="notification-mails">
<oc-checkbox
:model-value="disableEmailNotificationsValue"
size="large"
:label="$gettext('Receive notification mails')"
data-testid="account-page-notification-mails-checkbox"
@update:model-value="updateDisableEmailNotifications"
/>
</dd>
</div>
</dl>
</main>
</template>

<script lang="ts">
import { mapActions } from 'vuex'
import EditPasswordModal from '../components/EditPasswordModal.vue'
import { computed, defineComponent, onMounted, unref } from 'vue'
import { computed, defineComponent, onMounted, unref, ref } from 'vue'
import {
useAccessToken,
useCapabilityGraphPersonalDataExport,
useCapabilitySpacesEnabled,
useClientService,
useStore
} from 'web-pkg/src/composables'
import { useTask } from 'vue-concurrency'
import axios from 'axios'
import { v4 as uuidV4 } from 'uuid'
import { useGettext } from 'vue3-gettext'
import { setCurrentLanguage } from 'web-runtime/src/helpers/language'
import GdprExport from 'web-runtime/src/components/Account/GdprExport.vue'
import { useConfigurationManager } from 'web-pkg/src/composables/configuration'
import { isPersonalSpaceResource } from 'web-client/src/helpers'
import AppLoadingSpinner from 'web-pkg/src/components/AppLoadingSpinner.vue'

export default defineComponent({
name: 'Personal',
components: {
AppLoadingSpinner,
EditPasswordModal,
GdprExport
},
setup() {
const store = useStore()
const accessToken = useAccessToken({ store })
const language = useGettext()
const { $gettext } = language
const clientService = useClientService()
const configurationManager = useConfigurationManager()
const valuesList = ref()
const bundlesList = ref()
const selectedLanguageValue = ref()
const disableEmailNotificationsValue = ref()

// FIXME: Use graph capability when we have it
const isLanguageSupported = useCapabilitySpacesEnabled()
const isSettingsServiceSupported = useCapabilitySpacesEnabled()
const isChangePasswordEnabled = useCapabilitySpacesEnabled()
const isPersonalDataExportEnabled = useCapabilityGraphPersonalDataExport()
const user = computed(() => {
Expand All @@ -152,113 +171,196 @@ export default defineComponent({
)
})

const loadAccountBundleTask = useTask(function* () {
const loadValuesList = useTask(function* () {
try {
const {
data: { values }
} = yield clientService.httpAuthenticated.post('/api/v0/settings/values-list', {
account_uuid: 'me'
})
valuesList.value = values || []
} catch (e) {
console.error(e)
store.dispatch('showMessage', {
title: $gettext('Unable to load account data…'),
status: 'danger'
})
valuesList.value = []
}
}).restartable()

const loadBundlesList = useTask(function* () {
try {
const {
data: { bundles }
} = yield axios.post(
'/api/v0/settings/bundles-list',
{},
{
headers: {
authorization: `Bearer ${unref(accessToken)}`,
'X-Request-ID': uuidV4()
}
}
)
} = yield clientService.httpAuthenticated.post('/api/v0/settings/bundles-list', {})
bundlesList.value = bundles.find((b) => b.extension === 'ocis-accounts')
return bundles.find((b) => b.extension === 'ocis-accounts')
} catch (e) {
console.error(e)
return []
store.dispatch('showMessage', {
title: $gettext('Unable to load account data…'),
status: 'danger'
})
bundlesList.value = []
}
}).restartable()

const accountSettingIdentifier = {
extension: 'ocis-accounts',
bundle: 'profile',
setting: 'language'
}
const languageSetting = computed(() => {
return store.getters.getSettingsValue(accountSettingIdentifier)
const isLoading = computed(() => {
if (!isSettingsServiceSupported) {
return false
}
return (
loadValuesList.isRunning ||
!loadValuesList.last ||
loadBundlesList.isRunning ||
!loadBundlesList.last
)
})

const languageOptions = computed(() => {
const languageOptions = loadAccountBundleTask.last?.value?.settings.find(
(s) => s.name === 'language'
)?.singleChoiceValue.options
const languageOptions = unref(bundlesList).settings.find((s) => s.name === 'language')
?.singleChoiceValue.options
return languageOptions?.map((l) => ({
label: l.displayValue,
value: l.value.stringValue,
default: l.default
}))
})
const selectedLanguageOption = computed(() => {
const current = unref(languageSetting)?.listValue.values[0].stringValue
if (!current) {
return unref(languageOptions).find((o) => o.default)

const groupNames = computed(() => {
if (unref(useCapabilitySpacesEnabled())) {
return unref(user)
.groups.map((group) => group.displayName)
.join(', ')
}
return unref(languageOptions).find((o) => o.value === current)

return unref(user).groups.join(', ')
})
const updateSelectedLanguage = (option) => {
const bundle = loadAccountBundleTask.last?.value

const saveValue = async ({
identifier,
valueOptions
}: {
identifier: string
valueOptions: Record<string, any>
}) => {
const valueId = unref(valuesList).find((cV) => cV.identifier.setting === identifier)?.value
?.id

const value = {
bundleId: bundle?.id,
settingId: bundle?.settings.find((s) => s.name === 'language')?.id,
bundleId: unref(bundlesList)?.id,
settingId: unref(bundlesList)?.settings.find((s) => s.name === identifier)?.id,
resource: { type: 'TYPE_USER' },
listValue: { values: [{ stringValue: option.value }] },
...(unref(languageSetting) && { id: unref(languageSetting).id })
accountUuid: 'me',
...valueOptions,
...(valueId && { id: valueId })
}

axios.post(
'/api/v0/settings/values-save',
{ value: { ...value, accountUuid: 'me' } },
{
headers: {
authorization: `Bearer ${unref(accessToken)}`,
'X-Request-ID': uuidV4()
try {
await clientService.httpAuthenticated.post('/api/v0/settings/values-save', {
value: {
accountUuid: 'me',
...value
}
})

/**
* Edge case: we need to reload the values list to retrieve the valueId if not set,
* otherwise the backend saves multiple entries
*/
if (!valueId) {
loadValuesList.perform()
}
)

const newSetting = { identifier: accountSettingIdentifier, value }
store.commit('SET_SETTINGS_VALUE', newSetting)
setCurrentLanguage({ language, languageSetting: newSetting })
return value
} catch (e) {
throw e
}
}
const accountEditLink = computed(() => {
return store.getters.configuration?.options?.accountEditLink
})

const groupNames = computed(() => {
if (unref(useCapabilitySpacesEnabled())) {
return unref(user)
.groups.map((group) => group.displayName)
.join(', ')
const updateSelectedLanguage = async (option) => {
try {
const value = await saveValue({
identifier: 'language',
valueOptions: { listValue: { values: [{ stringValue: option.value }] } }
})
selectedLanguageValue.value = option
setCurrentLanguage({
language,
languageSetting: {
identifier: {
extension: 'ocis-accounts',
bundle: 'profile',
setting: 'language'
},
value
}
})
} catch (e) {
console.error(e)
store.dispatch('showMessage', {
title: $gettext('Unable to save language…'),
status: 'danger'
})
}
}

return unref(user).groups.join(', ')
})
const updateDisableEmailNotifications = async (option) => {
try {
await saveValue({
identifier: 'disable email notifications',
valueOptions: { boolValue: !option }
})
disableEmailNotificationsValue.value = option
} catch (e) {
console.error(e)
store.dispatch('showMessage', {
title: $gettext('Unable to save notifications email setting…'),
status: 'danger'
})
}
}

const logoutUrl = computed(() => {
return configurationManager.logoutUrl
})
onMounted(async () => {
if (unref(isSettingsServiceSupported)) {
await loadBundlesList.perform()
await loadValuesList.perform()

const languageConfiguration = unref(valuesList).find(
(cV) => cV.identifier.setting === 'language'
)
selectedLanguageValue.value = languageConfiguration
? unref(languageOptions).find(
(lO) => lO.value === languageConfiguration.value?.listValue?.values?.[0]?.stringValue
)
: unref(languageOptions).find((o) => o.default)

const disableEmailNotificationsConfiguration = unref(valuesList).find(
(cV) => cV.identifier.setting === 'disable email notifications'
AlexAndBear marked this conversation as resolved.
Show resolved Hide resolved
)

onMounted(() => {
if (unref(isLanguageSupported)) {
loadAccountBundleTask.perform()
disableEmailNotificationsValue.value = disableEmailNotificationsConfiguration
? !disableEmailNotificationsConfiguration.value?.boolValue
: true
}
})

return {
clientService,
languageOptions,
selectedLanguageOption,
selectedLanguageValue,
updateSelectedLanguage,
accountEditLink,
updateDisableEmailNotifications,
accountEditLink: store.getters.configuration?.options?.accountEditLink,
isChangePasswordEnabled,
showGdprExport,
isLanguageSupported,
isSettingsServiceSupported,
groupNames,
user,
logoutUrl
logoutUrl: configurationManager.logoutUrl,
isLoading,
disableEmailNotificationsValue
}
},
data() {
Expand Down
1 change: 0 additions & 1 deletion packages/web-runtime/src/services/auth/userManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ export class UserManager extends OidcUserManager {
let roles
;[graphUser, roles] = await Promise.all([graphClient.users.getMe(), this.fetchRoles()])
this.store.commit('SET_ROLES', roles)
this.store.commit('SET_SETTINGS_VALUES', settings)

role = await this.fetchRole({ graphUser, roles })
} else {
Expand Down
2 changes: 0 additions & 2 deletions packages/web-runtime/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import apps from './apps'
import auth from './auth'
import config from './config'
import user from './user'
import settings from './settings'
import modal from './modal'
import navigation from './navigation'
import spaces from './spaces'
Expand All @@ -24,7 +23,6 @@ export default {
apps,
user,
config,
settings,
modal,
navigation,
runtime
Expand Down
Loading