Ensure HeaderBar and Profile view use the same profile object so avatar gets synced

This commit is contained in:
Johannes Zellner
2025-07-16 18:32:06 +02:00
parent 5967907f86
commit b1752de36f
4 changed files with 29 additions and 27 deletions
+15 -20
View File
@@ -3,7 +3,7 @@
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
import { ref, onMounted, useTemplateRef } from 'vue';
import { ref, onMounted, useTemplateRef, inject } from 'vue';
import { Button, SingleSelect, Dialog, InputDialog, TextInput, InputGroup, FormGroup } from '@cloudron/pankow';
import { setLanguage } from '../i18n.js';
import { TOKEN_TYPES } from '../constants.js';
@@ -28,8 +28,10 @@ const profileModel = ProfileModel.create();
const cloudronModel = CloudronModel.create();
const tokensModel = TokensModel.create();
const profile = inject('profile');
const refreshProfile = inject('refreshProfile');
const config = ref({});
const user = ref({});
const inputDialog = useTemplateRef('inputDialog');
const primaryEmailDialog = useTemplateRef('primaryEmailDialog');
const fallbackEmailDialog = useTemplateRef('fallbackEmailDialog');
@@ -46,13 +48,6 @@ async function onSelectLanguage(lang) {
await setLanguage(lang);
}
async function refreshProfile() {
const [error, result] = await profileModel.get();
if (error) return console.error(error);
user.value = result;
}
// Profile edits
async function onChangeDisplayName(currentDisplayName) {
const displayName = await inputDialog.value.prompt({
@@ -216,43 +211,43 @@ onMounted(async () => {
<div style="display: flex; flex-wrap: wrap; gap: 20px">
<div style="width: 128px;">
<ImagePicker :src="user.avatarUrl" fallback-src="/img/avatar-default-symbolic.svg" :disabled="config.profileLocked" :size="512" :save-handler="onAvatarSubmit" display-width="128px"/>
<ImagePicker :src="profile.avatarUrl" fallback-src="/img/avatar-default-symbolic.svg" :disabled="config.profileLocked" :size="512" :save-handler="onAvatarSubmit" display-width="128px"/>
</div>
<div style="flex-grow: 1;">
<SettingsItem>
<FormGroup>
<label>{{ $t('main.username') }}</label>
<div>{{ user.username }}</div>
<div>{{ profile.username }}</div>
</FormGroup>
</SettingsItem>
<SettingsItem>
<FormGroup>
<label>{{ $t('main.displayName') }}</label>
<div>{{ user.displayName }}</div>
<div>{{ profile.displayName }}</div>
</FormGroup>
<div style="display: flex; align-items: center">
<Button tool plain @click="onChangeDisplayName(user.displayName)" v-show="user.username && !user.source && !config.profileLocked">{{ $t('main.dialog.edit') }}</Button>
<Button tool plain @click="onChangeDisplayName(profile.displayName)" v-show="profile.username && !profile.source && !config.profileLocked">{{ $t('main.dialog.edit') }}</Button>
</div>
</SettingsItem>
<SettingsItem>
<FormGroup>
<label>{{ $t('profile.primaryEmail') }}</label>
<div>{{ user.email }}</div>
<div>{{ profile.email }}</div>
</FormGroup>
<div style="display: flex; align-items: center">
<Button tool plain @click="onChangeEmail(user.email)" v-show="user.username && !user.source && !config.profileLocked">{{ $t('main.dialog.edit') }}</Button>
<Button tool plain @click="onChangeEmail(profile.email)" v-show="profile.username && !profile.source && !config.profileLocked">{{ $t('main.dialog.edit') }}</Button>
</div>
</SettingsItem>
<SettingsItem>
<FormGroup>
<label>{{ $t('profile.passwordRecoveryEmail') }}</label>
<div>{{ user.fallbackEmail || 'unset' }}</div>
<div>{{ profile.fallbackEmail || 'unset' }}</div>
</FormGroup>
<div style="display: flex; align-items: center">
<Button tool plain @click="onChangeFallbackEmail(user.fallbackEmail)" v-show="user.username && !user.source && !config.profileLocked">{{ $t('main.dialog.edit') }}</Button>
<Button tool plain @click="onChangeFallbackEmail(profile.fallbackEmail)" v-show="profile.username && !profile.source && !config.profileLocked">{{ $t('main.dialog.edit') }}</Button>
</div>
</SettingsItem>
@@ -267,15 +262,15 @@ onMounted(async () => {
<div style="display: flex; gap: 10px;">
<Button tool @click="onPasswordChange()">{{ $t('profile.changePasswordAction') }}</Button>
<Button tool v-show="!user.source && !config.external2FA" @click="user.twoFactorAuthenticationEnabled ? onTwoFADisable() : onOpenTwoFASetupDialog()">{{ $t(user.twoFactorAuthenticationEnabled ? 'profile.disable2FAAction' : 'profile.enable2FAAction') }}</Button>
<Button tool v-show="!profile.source && !config.external2FA" @click="profile.twoFactorAuthenticationEnabled ? onTwoFADisable() : onOpenTwoFASetupDialog()">{{ $t(profile.twoFactorAuthenticationEnabled ? 'profile.disable2FAAction' : 'profile.enable2FAAction') }}</Button>
</div>
</div>
</div>
</Section>
<NotificationSettings v-if="user.isAtLeastAdmin"/>
<NotificationSettings v-if="profile.isAtLeastAdmin"/>
<AppPasswords/>
<ApiTokens v-if="user.isAtLeastAdmin"/>
<ApiTokens v-if="profile.isAtLeastAdmin"/>
<Section :title="$t('profile.loginTokens.title')">
<p>{{ $t('profile.loginTokens.description', { webadminTokenCount: webadminTokens.length, cliTokenCount: cliTokens.length }) }}</p>