Files
ComfyUI_frontend/src/components/topbar/CurrentUserPopover.vue
Benjamin Lu 7bf8e5af38 feat(telemetry): add tracking for sidebar, run menu, dialogs, subgraphs, settings and credits (#6511)
Summary
- Add comprehensive telemetry for key UI interactions using existing
telemetry hooks (cloud-enabled, no-op in OSS):

Sidebar and top-level
- Node library button: `node_library`
- Model library button: `model_library`
- Workflows button: `workflows`
- Assets/Media button: `assets`
- Templates button: `templates`
- Keyboard Shortcuts: `keyboard_shortcuts`
- Console: `console`
- Help Center: `help_center`
- Settings (from Comfy logo menu): `settings_menu`

Floating canvas menu
- Minimap toggle: `minimap_toggle`
- Hide links toggle: `hide_links`

Run button and queue
- Run button handle (drag): `run_button_handle`
- Run mode selection: `run_instant`, `run_on_change`
- Queue multiple: `queue_multiple` fires on each run when batch count >
1 (moved from batch-count-change to run-time, per guidance)

Error dialogs
- Close (X/mask/ESC): `error_dialog_close` via dialog onClose
- Show report: `error_show_report`
- Help fix this: `error_help_fix_this`
- Find issues: `error_find_issues`

Nodes / Subgraphs
- Selection toolbox “Node info”: `node_info`
- Enter subgraph (node header enter): `open_subgraph`
- Subgraph breadcrumb navigation: `subgraph_breadcrumb_item` and
`subgraph_breadcrumb_root`

Settings / Credits / Search
- Settings menu button (under Comfy logo): `settings_menu`
- Purchase credits (Settings > Credits panel): tracked via existing
`trackAddApiCreditButtonClicked`
- Purchase credits (Avatar popover Top Up): tracked via existing
`trackAddApiCreditButtonClicked`
- Debounced search telemetry already present for node search and
template filters; left as-is

Notes and answers
- Error dialog onClose: only fires when the dialog actually closes (X,
mask, ESC, or programmatic close). “Show report” and “Help fix this” do
not close the dialog; they each emit their own events.
- Telemetry is behind the cloud provider; calls are optional
(`useTelemetry()?.…`). OSS builds send nothing.

Open questions / follow-ups
- Primary Run button click: today cloud-only `trackRunButton` exists; we
can also emit a UI-level `run` click (`UI_BUTTON_CLICKED` style)
alongside it if desired. Confirm preference and I can add it.
- Subgraph usage richness: if we want structured analytics (e.g.,
action, depth, subgraph id, node count), I can add a dedicated provider
method and include richer metadata at enter/breadcrumb.
- Optional parity: track the Comfy menu’s “Browse Templates” item in
addition to the sidebar Templates button.

Quality
- Ran `pnpm lint:fix` and `pnpm typecheck`; both pass locally.

Implementation details
- All handlers refactored to named functions where needed; used `void`
for intentionally unawaited async calls per lint rules.
- Event names kept consistent in `button_id` strings; happy to align to
a different naming scheme if you prefer.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6511-feat-telemetry-add-tracking-for-sidebar-run-menu-dialogs-subgraphs-settings-and-cre-29e6d73d365081a1b8b4fdfbbf40e18b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-01 23:09:41 -07:00

167 lines
4.2 KiB
Vue

<!-- 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">
<UserAvatar
class="mb-3"
:photo-url="userPhotoUrl"
:pt:icon:class="{
'text-2xl!': !userPhotoUrl
}"
size="large"
/>
<!-- User Details -->
<h3 class="my-0 mb-1 truncate text-lg font-semibold">
{{ userDisplayName || $t('g.user') }}
</h3>
<p v-if="userEmail" class="my-0 truncate text-sm text-muted">
{{ userEmail }}
</p>
</div>
</div>
<div v-if="isActiveSubscription" class="flex items-center justify-between">
<div class="flex flex-col gap-1">
<UserCredit text-class="text-2xl" />
<Button
:label="$t('subscription.partnerNodesCredits')"
severity="secondary"
text
size="small"
class="pl-6 p-0 h-auto justify-start"
:pt="{
root: {
class: 'hover:bg-transparent active:bg-transparent'
}
}"
@click="handleOpenPartnerNodesInfo"
/>
</div>
<Button
:label="$t('credits.topUp.topUp')"
severity="secondary"
size="small"
@click="handleTopUp"
/>
</div>
<SubscribeButton
v-else
:label="$t('subscription.subscribeToComfyCloud')"
size="small"
variant="gradient"
@subscribed="handleSubscribed"
/>
<Divider class="my-2" />
<Button
class="justify-start"
:label="$t('userSettings.title')"
icon="pi pi-cog"
text
fluid
severity="secondary"
@click="handleOpenUserSettings"
/>
<Button
v-if="isActiveSubscription"
class="justify-start"
:label="$t(planSettingsLabel)"
icon="pi pi-receipt"
text
fluid
severity="secondary"
@click="handleOpenPlanAndCreditsSettings"
/>
<Divider class="my-2" />
<Button
class="justify-start"
:label="$t('auth.signOut.signOut')"
icon="pi pi-sign-out"
text
fluid
severity="secondary"
@click="handleLogout"
/>
</div>
</template>
<script setup lang="ts">
import Button from 'primevue/button'
import Divider from 'primevue/divider'
import { onMounted } from 'vue'
import UserAvatar from '@/components/common/UserAvatar.vue'
import UserCredit from '@/components/common/UserCredit.vue'
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
import { useDialogService } from '@/services/dialogService'
const emit = defineEmits<{
close: []
}>()
const planSettingsLabel = isCloud
? 'settingsCategories.PlanCredits'
: 'settingsCategories.Credits'
const { userDisplayName, userEmail, userPhotoUrl, handleSignOut } =
useCurrentUser()
const authActions = useFirebaseAuthActions()
const dialogService = useDialogService()
const { isActiveSubscription, fetchStatus } = useSubscription()
const handleOpenUserSettings = () => {
dialogService.showSettingsDialog('user')
emit('close')
}
const handleOpenPlanAndCreditsSettings = () => {
if (isCloud) {
dialogService.showSettingsDialog('subscription')
} else {
dialogService.showSettingsDialog('credits')
}
emit('close')
}
const handleTopUp = () => {
// Track purchase credits entry from avatar popover
useTelemetry()?.trackAddApiCreditButtonClicked()
dialogService.showTopUpCreditsDialog()
emit('close')
}
const handleOpenPartnerNodesInfo = () => {
window.open(
'https://docs.comfy.org/tutorials/api-nodes/overview#api-nodes',
'_blank'
)
emit('close')
}
const handleLogout = async () => {
await handleSignOut()
emit('close')
}
const handleSubscribed = async () => {
await fetchStatus()
}
onMounted(() => {
void authActions.fetchBalance()
})
</script>