- abstract HeaderBar component

Signed-off-by: Christopher Ng <chrng8@gmail.com>
This commit is contained in:
Christopher Ng 2021-08-14 00:52:08 +00:00
parent 54944822f1
commit 0552b6a7dd
5 changed files with 286 additions and 39 deletions

View file

@ -0,0 +1,151 @@
<!--
- @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="language">
<select
id="language"
ref="language"
name="language"
:placeholder="t('settings', 'Language')"
required
@input="onLanguageChange">
<option v-for="commonLanguage in commonLanguages"
:key="commonLanguage.code"
:selected="language.code === commonLanguage.code"
:value="commonLanguage.code">
{{ commonLanguage.name }}
</option>
<optgroup label="" />
<option v-for="otherLanguage in otherLanguages"
:key="otherLanguage.code"
:selected="language.code === otherLanguage.code"
:value="otherLanguage.code">
{{ otherLanguage.name }}
</option>
</select>
<a
href="https://www.transifex.com/nextcloud/nextcloud/"
target="_blank"
rel="noreferrer noopener">
<em>{{ t('settings', 'Help translate') }}</em>
</a>
</div>
</template>
<script>
import { showError } from '@nextcloud/dialogs'
import { saveLanguage } from '../../../service/PersonalInfo/LanguageService'
export default {
name: 'Language',
props: {
commonLanguages: {
type: Array,
required: true,
},
otherLanguages: {
type: Array,
required: true,
},
language: {
type: Object,
required: true,
},
},
data() {
return {
initialLanguage: this.language,
}
},
computed: {
allLanguages() {
return Object.freeze(
[...this.commonLanguages, ...this.otherLanguages]
.reduce((acc, { code, name }) => ({ ...acc, [code]: name }), {})
)
},
},
methods: {
async onLanguageChange(e) {
const language = this.constructLanguage(e.target.value)
this.$emit('update:language', language)
if (this.$refs.language?.checkValidity()) {
await this.updateLanguage(language)
}
},
async updateLanguage(language) {
try {
const responseData = await saveLanguage(language.code)
this.handleResponse({
language,
status: responseData.ocs?.meta?.status,
})
this.reloadPage()
} catch (e) {
this.handleResponse({
errorMessage: 'Unable to update language',
error: e,
})
}
},
constructLanguage(languageCode) {
return {
code: languageCode,
name: this.allLanguages[languageCode],
}
},
handleResponse({ language, status, errorMessage, error }) {
if (status === 'ok') {
// Ensure that local state reflects server state
this.initialLanguage = language
} else {
showError(t('settings', errorMessage))
this.logger.error(errorMessage, error)
}
},
reloadPage() {
location.reload()
},
},
}
</script>
<style lang="scss" scoped>
.language {
display: grid;
a {
width: max-content;
}
}
</style>

View file

@ -0,0 +1,102 @@
<!--
- @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="language"
:is-valid-form="isValidForm" />
<template v-if="isEditable">
<Language
:common-languages="commonLanguages"
:other-languages="otherLanguages"
:language.sync="language"
@update:language="onUpdateLanguage" />
</template>
<span v-else>
{{ t('settings', 'No language set') }}
</span>
</form>
</template>
<script>
import { loadState } from '@nextcloud/initial-state'
import Language from './Language'
import HeaderBar from '../shared/HeaderBar'
import { SETTING_PROPERTY_READABLE_ENUM } from '../../../constants/AccountPropertyConstants'
const { languages: { activeLanguage, commonLanguages, otherLanguages } } = loadState('settings', 'personalInfoParameters', {})
export default {
name: 'LanguageSection',
components: {
Language,
HeaderBar,
},
data() {
return {
accountProperty: SETTING_PROPERTY_READABLE_ENUM.LANGUAGE,
isValidForm: true,
commonLanguages,
otherLanguages,
language: activeLanguage,
}
},
computed: {
isEditable() {
return Boolean(this.language)
},
},
mounted() {
this.$nextTick(() => this.updateFormValidity())
},
methods: {
onUpdateLanguage() {
this.$nextTick(() => this.updateFormValidity())
},
updateFormValidity() {
this.isValidForm = this.$refs.form?.checkValidity()
},
},
}
</script>
<style lang="scss" scoped>
form::v-deep button {
&:disabled {
cursor: default;
}
}
</style>

View file

@ -20,18 +20,21 @@
-->
<template>
<h3>
<h3
:class="{ 'setting-property': isSettingProperty }">
<label :for="labelFor">
<!-- Already translated as required by prop validator -->
{{ accountProperty }}
</label>
<FederationControl
class="federation-control"
:account-property="accountProperty"
:handle-scope-change="handleScopeChange"
:scope.sync="localScope"
@update:scope="onScopeChange" />
<template v-if="scope && handleScopeChange">
<FederationControl
class="federation-control"
:account-property="accountProperty"
:handle-scope-change="handleScopeChange"
:scope.sync="localScope"
@update:scope="onScopeChange" />
</template>
<template v-if="isEditable && isMultiValueSupported">
<AddButton
@ -46,7 +49,7 @@
import AddButton from './AddButton'
import FederationControl from './FederationControl'
import { ACCOUNT_PROPERTY_READABLE_ENUM } from '../../../constants/AccountPropertyConstants'
import { ACCOUNT_PROPERTY_READABLE_ENUM, SETTING_PROPERTY_READABLE_ENUM } from '../../../constants/AccountPropertyConstants'
export default {
name: 'HeaderBar',
@ -60,11 +63,11 @@ export default {
accountProperty: {
type: String,
required: true,
validator: (value) => Object.values(ACCOUNT_PROPERTY_READABLE_ENUM).includes(value),
validator: (value) => Object.values(ACCOUNT_PROPERTY_READABLE_ENUM).includes(value) || Object.values(SETTING_PROPERTY_READABLE_ENUM).includes(value),
},
handleScopeChange: {
type: Function,
required: true,
default: null,
},
isEditable: {
type: Boolean,
@ -84,7 +87,7 @@ export default {
},
scope: {
type: String,
required: true,
default: null,
},
},
@ -94,6 +97,12 @@ export default {
}
},
computed: {
isSettingProperty() {
return Object.values(SETTING_PROPERTY_READABLE_ENUM).includes(this.accountProperty)
},
},
methods: {
onAddAdditional() {
this.$emit('add-additional')
@ -119,6 +128,15 @@ export default {
}
}
h3.setting-property {
width: 100%;
min-height: 38px;
display: inline-flex;
position: relative;
flex-wrap: nowrap;
justify-content: flex-start;
}
.federation-control {
margin: -12px 0 0 8px;
}

View file

@ -29,8 +29,8 @@ import logger from './logger'
import DisplayNameSection from './components/PersonalInfo/DisplayNameSection/DisplayNameSection'
import EmailSection from './components/PersonalInfo/EmailSection/EmailSection'
import LanguageSection from './components/PersonalInfo/LanguageSection/LanguageSection'
// eslint-disable-next-line camelcase
__webpack_nonce__ = btoa(getRequestToken())
Vue.mixin({
@ -44,6 +44,8 @@ Vue.mixin({
const DisplayNameView = Vue.extend(DisplayNameSection)
const EmailView = Vue.extend(EmailSection)
const LanguageView = Vue.extend(LanguageSection)
new DisplayNameView().$mount('#vue-displaynamesection')
new EmailView().$mount('#vue-emailsection')
new LanguageView().$mount('#vue-languagesection')

View file

@ -245,33 +245,7 @@ script('settings', [
<div class="profile-settings-container">
<div class="personal-settings-setting-box personal-settings-language-box">
<?php if (isset($_['activelanguage'])) { ?>
<form id="language" class="section">
<h3>
<label for="languageinput"><?php p($l->t('Language'));?></label>
</h3>
<select id="languageinput" name="lang" data-placeholder="<?php p($l->t('Language'));?>">
<option value="<?php p($_['activelanguage']['code']);?>">
<?php p($_['activelanguage']['name']);?>
</option>
<?php foreach ($_['commonlanguages'] as $language):?>
<option value="<?php p($language['code']);?>">
<?php p($language['name']);?>
</option>
<?php endforeach;?>
<optgroup label=""></optgroup>
<?php foreach ($_['languages'] as $language):?>
<option value="<?php p($language['code']);?>">
<?php p($language['name']);?>
</option>
<?php endforeach;?>
</select>
<a href="https://www.transifex.com/nextcloud/nextcloud/"
target="_blank" rel="noreferrer noopener">
<em><?php p($l->t('Help translate'));?></em>
</a>
</form>
<?php } ?>
<div id="vue-languagesection" class="section"></div>
</div>
<div class="personal-settings-setting-box personal-settings-locale-box">
<?php if (isset($_['activelocale'])) { ?>