feat(stage): add global button analytics directive (#2146)
This commit is contained in:
@@ -6,6 +6,7 @@ import NProgress from 'nprogress'
|
||||
|
||||
import { autoAnimatePlugin } from '@formkit/auto-animate/vue'
|
||||
import { isEnvTruthy } from '@proj-airi/stage-shared'
|
||||
import { trackButtonPlugin } from '@proj-airi/stage-ui/directives/track-button'
|
||||
import { MotionPlugin } from '@vueuse/motion'
|
||||
import { createPinia } from 'pinia'
|
||||
import { setupLayouts } from 'virtual:generated-layouts'
|
||||
@@ -60,6 +61,7 @@ createApp(App)
|
||||
.use(pinia)
|
||||
.use(i18n)
|
||||
.use(Tres)
|
||||
.use(trackButtonPlugin)
|
||||
.mount('#app')
|
||||
|
||||
if (import.meta.env.DEV && !import.meta.env.SSR) {
|
||||
|
||||
+19
-4
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useElectronEventaInvoke } from '@proj-airi/electron-vueuse'
|
||||
import { useAnalytics } from '@proj-airi/stage-ui/composables/use-analytics'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
@@ -21,18 +22,30 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
const uiStore = useControlsIslandStore()
|
||||
const enabled = computed(() => uiStore.fadeOnHoverEnabled)
|
||||
const { t } = useI18n()
|
||||
const { trackControlsIslandAction } = useAnalytics()
|
||||
|
||||
const requestNotice = useElectronEventaInvoke(noticeWindowEventa.openWindow)
|
||||
const NOTICE_WINDOW_ID = 'fade-on-hover'
|
||||
|
||||
function setFadeOnHoverEnabled(value: boolean) {
|
||||
if (value)
|
||||
uiStore.enableFadeOnHover()
|
||||
else
|
||||
uiStore.disableFadeOnHover()
|
||||
|
||||
trackControlsIslandAction({
|
||||
action: value ? 'enable_fade_on_hover' : 'disable_fade_on_hover',
|
||||
})
|
||||
}
|
||||
|
||||
async function handleToggle() {
|
||||
if (enabled.value) {
|
||||
uiStore.disableFadeOnHover()
|
||||
setFadeOnHoverEnabled(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (uiStore.dontShowItAgainNoticeFadeOnHover) {
|
||||
uiStore.enableFadeOnHover()
|
||||
setFadeOnHoverEnabled(true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -42,8 +55,9 @@ async function handleToggle() {
|
||||
route: '/notice/fade-on-hover',
|
||||
type: 'fade-on-hover',
|
||||
})
|
||||
if (acknowledged)
|
||||
uiStore.enableFadeOnHover()
|
||||
if (acknowledged) {
|
||||
setFadeOnHoverEnabled(true)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to open fade-on-hover notice:', error)
|
||||
@@ -55,6 +69,7 @@ async function handleToggle() {
|
||||
<ControlButtonTooltip>
|
||||
<ControlButton
|
||||
:button-style="props.buttonStyle"
|
||||
:aria-label="enabled ? t('tamagotchi.stage.controls-island.fade-on-hover.disable') : t('tamagotchi.stage.controls-island.fade-on-hover.enable')"
|
||||
:class="{ 'border-primary-300/70 shadow-[0_10px_24px_rgba(0,0,0,0.22)]': enabled }"
|
||||
@click="handleToggle"
|
||||
>
|
||||
|
||||
+70
-10
@@ -38,7 +38,7 @@ const { alwaysOnTop, controlsIslandIconSize } = storeToRefs(settingsStore)
|
||||
const openSettings = useElectronEventaInvoke(electronOpenSettings)
|
||||
const openChat = useElectronEventaInvoke(electronOpenChat)
|
||||
const isLinux = useElectronEventaInvoke(electron.app.isLinux)
|
||||
const closeWindow = useElectronEventaInvoke(electronAppQuit)
|
||||
const quitApp = useElectronEventaInvoke(electronAppQuit)
|
||||
const setAlwaysOnTop = useElectronEventaInvoke(electronWindowSetAlwaysOnTop)
|
||||
const centerMainWindow = useElectronEventaInvoke(electronCenterMainWindow)
|
||||
|
||||
@@ -94,6 +94,10 @@ function toggleAlwaysOnTop() {
|
||||
alwaysOnTop.value = !alwaysOnTop.value
|
||||
}
|
||||
|
||||
function toggleControls() {
|
||||
expanded.value = !expanded.value
|
||||
}
|
||||
|
||||
// Grouped classes for icon / border / padding and combined style class
|
||||
const adjustStyleClasses = computed(() => {
|
||||
let isLarge: boolean
|
||||
@@ -158,7 +162,12 @@ function resetMainWindowPosition() {
|
||||
|
||||
<div grid grid-cols-3 gap-2>
|
||||
<ControlButtonTooltip disable-hoverable-content>
|
||||
<ControlButton :button-style="adjustStyleClasses.button" @click="openSettings({ route: '/settings' })">
|
||||
<ControlButton
|
||||
v-track-button="{ name: 'controls_island_action', action: 'toggle_settings' }"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:aria-label="t('tamagotchi.stage.controls-island.open-settings')"
|
||||
@click="openSettings({ route: '/settings' })"
|
||||
>
|
||||
<div i-solar:settings-minimalistic-outline :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300" />
|
||||
</ControlButton>
|
||||
<template #tooltip>
|
||||
@@ -169,7 +178,12 @@ function resetMainWindowPosition() {
|
||||
<ControlButtonTooltip disable-hoverable-content>
|
||||
<ControlsIslandProfilePicker placement="up" :open="blockingOverlays.has('profile-picker')" @update:open="setOverlay('profile-picker', $event)">
|
||||
<template #default="{ toggle }">
|
||||
<ControlButton :button-style="adjustStyleClasses.button" @click="toggle">
|
||||
<ControlButton
|
||||
v-track-button="{ name: 'controls_island_action', action: 'toggle_profile_picker' }"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:aria-label="t('tamagotchi.stage.controls-island.switch-profile')"
|
||||
@click="toggle"
|
||||
>
|
||||
<div i-solar:emoji-funny-square-broken :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300" />
|
||||
</ControlButton>
|
||||
</template>
|
||||
@@ -180,7 +194,12 @@ function resetMainWindowPosition() {
|
||||
</ControlButtonTooltip>
|
||||
|
||||
<ControlButtonTooltip disable-hoverable-content>
|
||||
<ControlButton :button-style="adjustStyleClasses.button" @click="refreshWindow">
|
||||
<ControlButton
|
||||
v-track-button="{ name: 'controls_island_action', action: 'refresh_window' }"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:aria-label="t('tamagotchi.stage.controls-island.refresh')"
|
||||
@click="refreshWindow"
|
||||
>
|
||||
<div i-solar:refresh-linear :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300" />
|
||||
</ControlButton>
|
||||
<template #tooltip>
|
||||
@@ -189,7 +208,12 @@ function resetMainWindowPosition() {
|
||||
</ControlButtonTooltip>
|
||||
|
||||
<ControlButtonTooltip disable-hoverable-content>
|
||||
<ControlButton :button-style="adjustStyleClasses.button" @click="resetMainWindowPosition()">
|
||||
<ControlButton
|
||||
v-track-button="{ name: 'controls_island_action', action: 'center_main_window' }"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:aria-label="t('tamagotchi.stage.controls-island.center-main-window')"
|
||||
@click="resetMainWindowPosition"
|
||||
>
|
||||
<div i-solar:target-linear :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300" />
|
||||
</ControlButton>
|
||||
<template #tooltip>
|
||||
@@ -198,7 +222,15 @@ function resetMainWindowPosition() {
|
||||
</ControlButtonTooltip>
|
||||
|
||||
<ControlButtonTooltip disable-hoverable-content>
|
||||
<ControlButton :button-style="adjustStyleClasses.button" @click="toggleDark()">
|
||||
<ControlButton
|
||||
v-track-button="{
|
||||
name: 'controls_island_action',
|
||||
action: isDark ? 'switch_to_light_mode' : 'switch_to_dark_mode',
|
||||
}"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:aria-label="isDark ? t('tamagotchi.stage.controls-island.switch-to-light-mode') : t('tamagotchi.stage.controls-island.switch-to-dark-mode')"
|
||||
@click="() => toggleDark()"
|
||||
>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="isDark" i-solar:moon-outline :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300" />
|
||||
<div v-else i-solar:sun-2-outline :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300" />
|
||||
@@ -210,7 +242,15 @@ function resetMainWindowPosition() {
|
||||
</ControlButtonTooltip>
|
||||
|
||||
<ControlButtonTooltip disable-hoverable-content>
|
||||
<ControlButton :button-style="adjustStyleClasses.button" @click="toggleAlwaysOnTop()">
|
||||
<ControlButton
|
||||
v-track-button="{
|
||||
name: 'controls_island_action',
|
||||
action: alwaysOnTop ? 'unpin_from_top' : 'pin_on_top',
|
||||
}"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:aria-label="alwaysOnTop ? t('tamagotchi.stage.controls-island.unpin-from-top') : t('tamagotchi.stage.controls-island.pin-on-top')"
|
||||
@click="toggleAlwaysOnTop"
|
||||
>
|
||||
<div v-if="alwaysOnTop" i-solar:pin-bold :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300" />
|
||||
<div v-else i-solar:pin-linear :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300 opacity-50" />
|
||||
</ControlButton>
|
||||
@@ -222,7 +262,14 @@ function resetMainWindowPosition() {
|
||||
<ControlsIslandFadeOnHover :icon-class="adjustStyleClasses.icon" :button-style="adjustStyleClasses.button" />
|
||||
|
||||
<ControlButtonTooltip disable-hoverable-content>
|
||||
<ControlButton :button-style="adjustStyleClasses.button" hover:bg-red-500 hover:text-white @click="closeWindow()">
|
||||
<ControlButton
|
||||
v-track-button="{ name: 'controls_island_action', action: 'close_app' }"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:aria-label="t('tamagotchi.stage.controls-island.close')"
|
||||
hover:bg-red-500
|
||||
hover:text-white
|
||||
@click="() => quitApp()"
|
||||
>
|
||||
<div i-solar:close-circle-outline :class="adjustStyleClasses.icon" />
|
||||
</ControlButton>
|
||||
<template #tooltip>
|
||||
@@ -236,7 +283,15 @@ function resetMainWindowPosition() {
|
||||
<!-- Main Controls -->
|
||||
<div flex flex-col gap-1>
|
||||
<ControlButtonTooltip side="left">
|
||||
<ControlButton :button-style="adjustStyleClasses.button" @click="expanded = !expanded">
|
||||
<ControlButton
|
||||
v-track-button="{
|
||||
name: 'controls_island_action',
|
||||
action: expanded ? 'collapse_controls' : 'expand_controls',
|
||||
}"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:aria-label="expanded ? t('tamagotchi.stage.controls-island.collapse') : t('tamagotchi.stage.controls-island.expand')"
|
||||
@click="toggleControls"
|
||||
>
|
||||
<div
|
||||
:class="[adjustStyleClasses.icon, expanded ? 'rotate-180' : 'rotate-0']"
|
||||
i-solar:alt-arrow-up-line-duotone scale-110 transition-all duration-300
|
||||
@@ -249,7 +304,12 @@ function resetMainWindowPosition() {
|
||||
</ControlButtonTooltip>
|
||||
|
||||
<ControlButtonTooltip side="left">
|
||||
<ControlButton :button-style="adjustStyleClasses.button" @click="openChat">
|
||||
<ControlButton
|
||||
v-track-button="{ name: 'controls_island_action', action: 'toggle_chat' }"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:aria-label="t('tamagotchi.stage.controls-island.open-chat')"
|
||||
@click="() => openChat()"
|
||||
>
|
||||
<div i-solar:chat-line-line-duotone :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300" />
|
||||
</ControlButton>
|
||||
<template #tooltip>
|
||||
|
||||
@@ -5,6 +5,7 @@ import Tres from '@tresjs/core'
|
||||
|
||||
import { autoAnimatePlugin } from '@formkit/auto-animate/vue'
|
||||
import { PiniaColada } from '@pinia/colada'
|
||||
import { trackButtonPlugin } from '@proj-airi/stage-ui/directives/track-button'
|
||||
import { MotionPlugin } from '@vueuse/motion'
|
||||
import { createPinia } from 'pinia'
|
||||
import { setupLayouts } from 'virtual:generated-layouts'
|
||||
@@ -53,4 +54,5 @@ createApp(App)
|
||||
.use(PiniaColada)
|
||||
.use(i18n)
|
||||
.use(Tres)
|
||||
.use(trackButtonPlugin)
|
||||
.mount('#app')
|
||||
|
||||
@@ -33,7 +33,6 @@ const {
|
||||
} = useElectronAutoUpdater()
|
||||
|
||||
const {
|
||||
trackUpdateCheckClicked,
|
||||
trackUpdateDownloaded,
|
||||
trackUpdateInstallClicked,
|
||||
trackSettingsChanged,
|
||||
@@ -126,11 +125,6 @@ const isDowngradeUpdate = computed(() => {
|
||||
const getUpdaterPreferences = useElectronEventaInvoke(electronGetUpdaterPreferences)
|
||||
const setUpdaterPreferences = useElectronEventaInvoke(electronSetUpdaterPreferences)
|
||||
|
||||
function handleCheckForUpdates() {
|
||||
trackUpdateCheckClicked({ channel: selectedUpdateChannel.value })
|
||||
return checkForUpdates()
|
||||
}
|
||||
|
||||
function handleQuitAndInstall() {
|
||||
trackUpdateInstallClicked({ channel: selectedUpdateChannel.value, version: updateState.value.info?.version })
|
||||
quitAndInstall()
|
||||
@@ -384,6 +378,7 @@ onMounted(() => {
|
||||
|
||||
<div :class="['flex flex-wrap gap-2']">
|
||||
<Button
|
||||
v-track-button="{ name: 'update_check_clicked', channel: selectedUpdateChannel }"
|
||||
:variant="isError ? 'caution' : 'secondary'"
|
||||
:loading="isBusy"
|
||||
:disabled="isDisabled"
|
||||
@@ -397,7 +392,7 @@ onMounted(() => {
|
||||
: isError
|
||||
? t('tamagotchi.stage.about.update.actions.retry-check')
|
||||
: t('tamagotchi.stage.about.update.actions.check-for-updates')"
|
||||
@click="handleCheckForUpdates()"
|
||||
@click="checkForUpdates()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
} from './mcp-config'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { trackMcpServerAdded, trackMcpServerRemoved, trackMcpConnectionTestRun } = useAnalytics()
|
||||
const { trackMcpServerRemoved, trackMcpConnectionTestRun } = useAnalytics()
|
||||
const tn = (key: string, params?: Record<string, unknown>) => t(`settings.pages.modules.mcp-server.${key}`, params ?? {})
|
||||
|
||||
const invokeOpenConfigFile = useElectronEventaInvoke(electronMcpOpenConfigFile)
|
||||
@@ -215,7 +215,6 @@ function formatJsonDraft() {
|
||||
function addServer() {
|
||||
const server = createServerForm()
|
||||
servers.value.push(server)
|
||||
trackMcpServerAdded()
|
||||
if (!testRowId.value)
|
||||
testRowId.value = server.rowId
|
||||
}
|
||||
@@ -476,6 +475,7 @@ onMounted(async () => {
|
||||
</article>
|
||||
|
||||
<Button
|
||||
v-track-button="{ name: 'mcp_server_added' }"
|
||||
variant="secondary-muted" size="md" block :disabled="isBusy"
|
||||
icon="i-solar:add-circle-bold-duotone" :label="tn('actions.add-server')"
|
||||
@click="addServer"
|
||||
|
||||
@@ -7,6 +7,7 @@ import NProgress from 'nprogress'
|
||||
import { autoAnimatePlugin } from '@formkit/auto-animate/vue'
|
||||
import { PiniaColada } from '@pinia/colada'
|
||||
import { isEnvTruthy } from '@proj-airi/stage-shared'
|
||||
import { trackButtonPlugin } from '@proj-airi/stage-ui/directives/track-button'
|
||||
import { MotionPlugin } from '@vueuse/motion'
|
||||
import { createPinia } from 'pinia'
|
||||
import { setupLayouts } from 'virtual:generated-layouts'
|
||||
@@ -55,6 +56,7 @@ createApp(App)
|
||||
.use(PiniaColada)
|
||||
.use(i18n)
|
||||
.use(Tres)
|
||||
.use(trackButtonPlugin)
|
||||
.mount('#app')
|
||||
|
||||
if (import.meta.env.DEV && !import.meta.env.SSR) {
|
||||
|
||||
Reference in New Issue
Block a user