mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
Implement top menu user popover (#3631)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -1,45 +1,50 @@
|
||||
<!-- A button that shows current authenticated user's avatar -->
|
||||
<template>
|
||||
<Button
|
||||
v-if="isAuthenticated"
|
||||
v-tooltip="{ value: $t('userSettings.title'), showDelay: 300 }"
|
||||
class="user-profile-button p-1"
|
||||
severity="secondary"
|
||||
text
|
||||
:aria-label="$t('userSettings.title')"
|
||||
@click="openUserSettings"
|
||||
>
|
||||
<div
|
||||
class="flex items-center rounded-full bg-[var(--p-content-background)]"
|
||||
<div>
|
||||
<Button
|
||||
v-if="isAuthenticated"
|
||||
v-tooltip="{ value: $t('userSettings.title'), showDelay: 300 }"
|
||||
class="user-profile-button p-1"
|
||||
severity="secondary"
|
||||
text
|
||||
:aria-label="$t('userSettings.title')"
|
||||
@click="popover?.toggle($event)"
|
||||
>
|
||||
<Avatar
|
||||
:image="photoURL"
|
||||
:icon="photoURL ? undefined : 'pi pi-user'"
|
||||
shape="circle"
|
||||
aria-label="User Avatar"
|
||||
/>
|
||||
<div
|
||||
class="flex items-center rounded-full bg-[var(--p-content-background)]"
|
||||
>
|
||||
<Avatar
|
||||
:image="photoURL"
|
||||
:icon="photoURL ? undefined : 'pi pi-user'"
|
||||
shape="circle"
|
||||
aria-label="User Avatar"
|
||||
/>
|
||||
|
||||
<i class="pi pi-chevron-down px-1" :style="{ fontSize: '0.5rem' }" />
|
||||
</div>
|
||||
</Button>
|
||||
<i class="pi pi-chevron-down px-1" :style="{ fontSize: '0.5rem' }" />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<Popover ref="popover" :show-arrow="false">
|
||||
<CurrentUserPopover />
|
||||
</Popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Avatar from 'primevue/avatar'
|
||||
import Button from 'primevue/button'
|
||||
import { computed } from 'vue'
|
||||
import Popover from 'primevue/popover'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||
|
||||
import CurrentUserPopover from './CurrentUserPopover.vue'
|
||||
|
||||
const authStore = useFirebaseAuthStore()
|
||||
const dialogService = useDialogService()
|
||||
|
||||
const popover = ref<InstanceType<typeof Popover> | null>(null)
|
||||
const isAuthenticated = computed(() => authStore.isAuthenticated)
|
||||
const photoURL = computed<string | undefined>(
|
||||
() => authStore.currentUser?.photoURL ?? undefined
|
||||
)
|
||||
|
||||
const openUserSettings = () => {
|
||||
dialogService.showSettingsDialog('user')
|
||||
}
|
||||
</script>
|
||||
|
||||
80
src/components/topbar/CurrentUserPopover.vue
Normal file
80
src/components/topbar/CurrentUserPopover.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<!-- A popover that shows current user information and actions -->
|
||||
<template>
|
||||
<div class="current-user-popover w-72">
|
||||
<!-- User Info Section -->
|
||||
<div class="p-3">
|
||||
<div class="flex flex-col items-center">
|
||||
<Avatar
|
||||
class="mb-3"
|
||||
:image="user?.photoURL ?? undefined"
|
||||
:icon="user?.photoURL ? undefined : 'pi pi-user'"
|
||||
shape="circle"
|
||||
size="large"
|
||||
aria-label="User Avatar"
|
||||
/>
|
||||
|
||||
<!-- User Details -->
|
||||
<h3 class="text-lg font-semibold truncate my-0 mb-1">
|
||||
{{ user?.displayName || $t('g.user') }}
|
||||
</h3>
|
||||
<p v-if="user?.email" class="text-sm text-muted truncate my-0">
|
||||
{{ user.email }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider class="my-2" />
|
||||
|
||||
<Button
|
||||
class="justify-start"
|
||||
:label="$t('userSettings.title')"
|
||||
icon="pi pi-cog"
|
||||
text
|
||||
fluid
|
||||
severity="secondary"
|
||||
@click="handleOpenUserSettings"
|
||||
/>
|
||||
|
||||
<Divider class="my-2" />
|
||||
|
||||
<div class="w-full flex flex-col gap-2 p-2">
|
||||
<div class="text-muted text-sm">
|
||||
{{ $t('credits.yourCreditBalance') }}
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<UserCredit text-class="text-2xl" />
|
||||
<Button :label="$t('credits.topUp.topUp')" @click="handleTopUp" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Avatar from 'primevue/avatar'
|
||||
import Button from 'primevue/button'
|
||||
import Divider from 'primevue/divider'
|
||||
import { computed, onMounted } from 'vue'
|
||||
|
||||
import UserCredit from '@/components/common/UserCredit.vue'
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
import { useFirebaseAuthService } from '@/services/firebaseAuthService'
|
||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||
|
||||
const authStore = useFirebaseAuthStore()
|
||||
const authService = useFirebaseAuthService()
|
||||
const dialogService = useDialogService()
|
||||
|
||||
const user = computed(() => authStore.currentUser)
|
||||
|
||||
const handleOpenUserSettings = () => {
|
||||
dialogService.showSettingsDialog('user')
|
||||
}
|
||||
|
||||
const handleTopUp = () => {
|
||||
dialogService.showTopUpCreditsDialog()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void authService.fetchBalance()
|
||||
})
|
||||
</script>
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"g": {
|
||||
"user": "User",
|
||||
"currentUser": "Current user",
|
||||
"empty": "Empty",
|
||||
"noWorkflowsFound": "No workflows found.",
|
||||
@@ -1161,7 +1162,8 @@
|
||||
"quickPurchase": "Quick Purchase",
|
||||
"maxAmount": "(Max. $1,000 USD)",
|
||||
"buyNow": "Buy now",
|
||||
"seeDetails": "See details"
|
||||
"seeDetails": "See details",
|
||||
"topUp": "Top Up"
|
||||
}
|
||||
},
|
||||
"userSettings": {
|
||||
|
||||
@@ -114,7 +114,8 @@
|
||||
"insufficientTitle": "Créditos insuficientes",
|
||||
"maxAmount": "(Máx. $1,000 USD)",
|
||||
"quickPurchase": "Compra rápida",
|
||||
"seeDetails": "Ver detalles"
|
||||
"seeDetails": "Ver detalles",
|
||||
"topUp": "Recargar"
|
||||
},
|
||||
"yourCreditBalance": "Tu saldo de créditos"
|
||||
},
|
||||
@@ -302,6 +303,7 @@
|
||||
"updated": "Actualizado",
|
||||
"updating": "Actualizando",
|
||||
"upload": "Subir",
|
||||
"user": "Usuario",
|
||||
"videoFailedToLoad": "Falló la carga del video",
|
||||
"workflow": "Flujo de trabajo"
|
||||
},
|
||||
|
||||
@@ -114,7 +114,8 @@
|
||||
"insufficientTitle": "Crédits insuffisants",
|
||||
"maxAmount": "(Max. 1 000 $ US)",
|
||||
"quickPurchase": "Achat rapide",
|
||||
"seeDetails": "Voir les détails"
|
||||
"seeDetails": "Voir les détails",
|
||||
"topUp": "Recharger"
|
||||
},
|
||||
"yourCreditBalance": "Votre solde de crédits"
|
||||
},
|
||||
@@ -302,6 +303,7 @@
|
||||
"updated": "Mis à jour",
|
||||
"updating": "Mise à jour",
|
||||
"upload": "Téléverser",
|
||||
"user": "Utilisateur",
|
||||
"videoFailedToLoad": "Échec du chargement de la vidéo",
|
||||
"workflow": "Flux de travail"
|
||||
},
|
||||
|
||||
@@ -114,7 +114,8 @@
|
||||
"insufficientTitle": "クレジット不足",
|
||||
"maxAmount": "(最大 $1,000 USD)",
|
||||
"quickPurchase": "クイック購入",
|
||||
"seeDetails": "詳細を見る"
|
||||
"seeDetails": "詳細を見る",
|
||||
"topUp": "チャージ"
|
||||
},
|
||||
"yourCreditBalance": "あなたのクレジット残高"
|
||||
},
|
||||
@@ -302,6 +303,7 @@
|
||||
"updated": "更新済み",
|
||||
"updating": "更新中",
|
||||
"upload": "アップロード",
|
||||
"user": "ユーザー",
|
||||
"videoFailedToLoad": "ビデオの読み込みに失敗しました",
|
||||
"workflow": "ワークフロー"
|
||||
},
|
||||
|
||||
@@ -114,7 +114,8 @@
|
||||
"insufficientTitle": "크레딧 부족",
|
||||
"maxAmount": "(최대 $1,000 USD)",
|
||||
"quickPurchase": "빠른 구매",
|
||||
"seeDetails": "자세히 보기"
|
||||
"seeDetails": "자세히 보기",
|
||||
"topUp": "충전하기"
|
||||
},
|
||||
"yourCreditBalance": "보유 크레딧 잔액"
|
||||
},
|
||||
@@ -302,6 +303,7 @@
|
||||
"updated": "업데이트 됨",
|
||||
"updating": "업데이트 중",
|
||||
"upload": "업로드",
|
||||
"user": "사용자",
|
||||
"videoFailedToLoad": "비디오를 로드하지 못했습니다.",
|
||||
"workflow": "워크플로"
|
||||
},
|
||||
|
||||
@@ -114,7 +114,8 @@
|
||||
"insufficientTitle": "Недостаточно кредитов",
|
||||
"maxAmount": "(Макс. $1,000 USD)",
|
||||
"quickPurchase": "Быстрая покупка",
|
||||
"seeDetails": "Смотреть детали"
|
||||
"seeDetails": "Смотреть детали",
|
||||
"topUp": "Пополнить"
|
||||
},
|
||||
"yourCreditBalance": "Ваш баланс кредитов"
|
||||
},
|
||||
@@ -302,6 +303,7 @@
|
||||
"updated": "Обновлено",
|
||||
"updating": "Обновление",
|
||||
"upload": "Загрузить",
|
||||
"user": "Пользователь",
|
||||
"videoFailedToLoad": "Не удалось загрузить видео",
|
||||
"workflow": "Рабочий процесс"
|
||||
},
|
||||
|
||||
@@ -114,7 +114,8 @@
|
||||
"insufficientTitle": "积分不足",
|
||||
"maxAmount": "(最高 $1,000 美元)",
|
||||
"quickPurchase": "快速购买",
|
||||
"seeDetails": "查看详情"
|
||||
"seeDetails": "查看详情",
|
||||
"topUp": "充值"
|
||||
},
|
||||
"yourCreditBalance": "您的积分余额"
|
||||
},
|
||||
@@ -302,6 +303,7 @@
|
||||
"updated": "已更新",
|
||||
"updating": "更新中",
|
||||
"upload": "上传",
|
||||
"user": "用户",
|
||||
"videoFailedToLoad": "视频加载失败",
|
||||
"workflow": "工作流"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user