Compare commits

...

1 Commits

Author SHA1 Message Date
Benjamin Lu
8e9158a57d feat: thread share_id through share telemetry funnel
Adds a single share_id join key across the share → open → load → run
funnel so the share virality funnel and "new users via shares"
acquisition metric become queryable.

- Sharer side: share_flow link_created/link_copied now carry share_id
- Recipient side: new app:share_link_opened event with share_id,
  is_authenticated, is_new_user (fired on valid ?share= param)
- workflow_imported/workflow_opened carry share_id via a new shareId
  option on loadGraphData
- Run attribution: session-scoped share provenance store records the
  imported workflow's share_id; getExecutionContext reads it so
  execution_start is attributable to its share
- Acquisition: trackAuth (sign_up/login) attaches the preserved ?share=
  id so new-users-via-shares is a direct query

Frontend-only — share_id already existed in the publish response and was
just never put on the wire. All three providers (Mixpanel, PostHog, GTM)
emit the new event and properties.

Fixes MAR-318

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 11:25:49 -07:00
16 changed files with 344 additions and 15 deletions

View File

@@ -87,6 +87,13 @@ export const capturePreservedQuery = (
writeToStorage(namespace, payload)
}
export const getPreservedQueryParam = (
namespace: string,
key: string
): string | undefined => {
return preservedQueries.get(namespace)?.[key]
}
export const mergePreservedQueryIntoQuery = (
namespace: string,
query?: LocationQueryRaw

View File

@@ -6,6 +6,7 @@ import type {
DefaultViewSetMetadata,
EnterLinearMetadata,
ShareFlowMetadata,
ShareLinkOpenedMetadata,
ExecutionErrorMetadata,
ExecutionSuccessMetadata,
ExecutionTriggerSource,
@@ -182,6 +183,10 @@ export class TelemetryRegistry implements TelemetryDispatcher {
this.dispatch((provider) => provider.trackShareFlow?.(metadata))
}
trackShareLinkOpened(metadata: ShareLinkOpenedMetadata): void {
this.dispatch((provider) => provider.trackShareLinkOpened?.(metadata))
}
trackPageVisibilityChanged(metadata: PageVisibilityMetadata): void {
this.dispatch((provider) => provider.trackPageVisibilityChanged?.(metadata))
}

View File

@@ -15,6 +15,7 @@ import type {
PageVisibilityMetadata,
SettingChangedMetadata,
ShareFlowMetadata,
ShareLinkOpenedMetadata,
SubscriptionMetadata,
SubscriptionSuccessMetadata,
SurveyResponses,
@@ -139,6 +140,7 @@ export class GtmTelemetryProvider implements TelemetryProvider {
const payload = {
method: metadata.method,
...(metadata.user_id ? { user_id: metadata.user_id } : {}),
...(metadata.share_id ? { share_id: metadata.share_id } : {}),
...(metadata.email
? {
user_data: {
@@ -287,7 +289,16 @@ export class GtmTelemetryProvider implements TelemetryProvider {
trackShareFlow(metadata: ShareFlowMetadata): void {
this.pushEvent('share_flow', {
step: metadata.step,
source: metadata.source
source: metadata.source,
share_id: metadata.share_id
})
}
trackShareLinkOpened(metadata: ShareLinkOpenedMetadata): void {
this.pushEvent('share_link_opened', {
share_id: metadata.share_id,
is_authenticated: metadata.is_authenticated,
is_new_user: metadata.is_new_user
})
}

View File

@@ -18,6 +18,7 @@ import type {
DefaultViewSetMetadata,
EnterLinearMetadata,
ShareFlowMetadata,
ShareLinkOpenedMetadata,
ExecutionContext,
ExecutionTriggerSource,
ExecutionErrorMetadata,
@@ -384,6 +385,10 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
this.trackEvent(TelemetryEvents.SHARE_FLOW, metadata)
}
trackShareLinkOpened(metadata: ShareLinkOpenedMetadata): void {
this.trackEvent(TelemetryEvents.SHARE_LINK_OPENED, metadata)
}
trackPageVisibilityChanged(metadata: PageVisibilityMetadata): void {
this.trackEvent(TelemetryEvents.PAGE_VISIBILITY_CHANGED, metadata)
}

View File

@@ -14,6 +14,7 @@ import type {
DefaultViewSetMetadata,
EnterLinearMetadata,
ShareFlowMetadata,
ShareLinkOpenedMetadata,
ExecutionContext,
ExecutionErrorMetadata,
ExecutionSuccessMetadata,
@@ -488,6 +489,10 @@ export class PostHogTelemetryProvider implements TelemetryProvider {
this.trackEvent(TelemetryEvents.SHARE_FLOW, metadata)
}
trackShareLinkOpened(metadata: ShareLinkOpenedMetadata): void {
this.trackEvent(TelemetryEvents.SHARE_LINK_OPENED, metadata)
}
trackPageVisibilityChanged(metadata: PageVisibilityMetadata): void {
this.trackEvent(TelemetryEvents.PAGE_VISIBILITY_CHANGED, metadata)
}

View File

@@ -29,6 +29,11 @@ export interface AuthMetadata {
utm_source?: string
utm_medium?: string
utm_campaign?: string
/**
* Share link id when the user authenticated while opening a shared
* workflow URL. Powers the "new users via shares" acquisition metric.
*/
share_id?: string
}
/**
@@ -97,6 +102,11 @@ export interface ExecutionContext {
toolkit_node_names: string[]
toolkit_node_count: number
trigger_source?: ExecutionTriggerSource
/**
* Share link id when the active workflow was imported from a shared URL
* this session. Makes shared workflow runs attributable to their share.
*/
share_id?: string
}
/**
@@ -165,6 +175,12 @@ export interface WorkflowImportMetadata {
| 'template'
| 'shared_url'
| 'unknown'
/**
* Share link id when the workflow was opened from a shared URL
* (open_source = 'shared_url'). Joins recipient-side events to the
* sharer-side share_flow link_created event.
*/
share_id?: string
}
export interface EnterLinearMetadata {
@@ -189,6 +205,16 @@ type ShareFlowStep =
export interface ShareFlowMetadata {
step: ShareFlowStep
source?: 'app_mode' | 'graph_mode'
share_id?: string
}
/**
* Recipient-side share link open metadata
*/
export interface ShareLinkOpenedMetadata {
share_id: string
is_authenticated: boolean
is_new_user?: boolean
}
/**
@@ -477,6 +503,7 @@ export interface TelemetryProvider {
trackDefaultViewSet?(metadata: DefaultViewSetMetadata): void
trackEnterLinear?(metadata: EnterLinearMetadata): void
trackShareFlow?(metadata: ShareFlowMetadata): void
trackShareLinkOpened?(metadata: ShareLinkOpenedMetadata): void
// Page visibility events
trackPageVisibilityChanged?(metadata: PageVisibilityMetadata): void
@@ -570,6 +597,7 @@ export const TelemetryEvents = {
WORKFLOW_OPENED: 'app:workflow_opened',
ENTER_LINEAR_MODE: 'app:app_mode_opened',
SHARE_FLOW: 'app:share_flow',
SHARE_LINK_OPENED: 'app:share_link_opened',
// Page Visibility
PAGE_VISIBILITY_CHANGED: 'app:page_visibility_changed',
@@ -649,6 +677,7 @@ export type TelemetryEventProperties =
| WorkflowCreatedMetadata
| EnterLinearMetadata
| ShareFlowMetadata
| ShareLinkOpenedMetadata
| WorkflowSavedMetadata
| DefaultViewSetMetadata
| SubscriptionMetadata

View File

@@ -8,6 +8,7 @@ const hoisted = vi.hoisted(() => ({
mockActiveWorkflow: null as null | {
filename: string
fullFilename: string
path?: string
},
mockKnownTemplateNames: new Set<string>(),
mockTemplateByName: null as null | { sourceModule?: string }
@@ -64,6 +65,8 @@ vi.mock('@/scripts/app', () => ({
app: { rootGraph: {} }
}))
import { recordShareProvenance } from '@/platform/workflow/sharing/utils/shareProvenance'
import { getExecutionContext } from './getExecutionContext'
describe('getExecutionContext', () => {
@@ -167,6 +170,33 @@ describe('getExecutionContext', () => {
expect(context.toolkit_node_names).toEqual(['ImageCrop'])
})
describe('share attribution', () => {
it('includes share_id when the active workflow was imported from a share link', () => {
hoisted.mockActiveWorkflow = {
filename: 'shared-workflow',
fullFilename: 'shared-workflow.json',
path: 'workflows/shared-workflow.json'
}
recordShareProvenance('workflows/shared-workflow.json', 'abc123def456')
const context = getExecutionContext()
expect(context.share_id).toBe('abc123def456')
})
it('omits share_id for workflows without share provenance', () => {
hoisted.mockActiveWorkflow = {
filename: 'my-own-workflow',
fullFilename: 'my-own-workflow.json',
path: 'workflows/my-own-workflow.json'
}
const context = getExecutionContext()
expect(context.share_id).toBeUndefined()
})
})
describe('template detection', () => {
it('detects a regular template by name', () => {
hoisted.mockKnownTemplateNames = new Set(['flux-dev'])

View File

@@ -3,6 +3,7 @@ import {
TOOLKIT_NODE_NAMES
} from '@/constants/toolkitNodes'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
import { getShareProvenance } from '@/platform/workflow/sharing/utils/shareProvenance'
import { useWorkflowTemplatesStore } from '@/platform/workflow/templates/repositories/workflowTemplatesStore'
import { app } from '@/scripts/app'
import { useNodeDefStore } from '@/stores/nodeDefStore'
@@ -28,6 +29,9 @@ export function getExecutionContext(): ExecutionContext {
const templatesStore = useWorkflowTemplatesStore()
const nodeDefStore = useNodeDefStore()
const activeWorkflow = workflowStore.activeWorkflow
const shareId = activeWorkflow
? getShareProvenance(activeWorkflow.path)
: undefined
const nodeCounts = reduceAllNodes<NodeMetrics>(
app.rootGraph,
@@ -101,6 +105,7 @@ export function getExecutionContext(): ExecutionContext {
template_models: englishMetadata?.models ?? template?.models,
template_use_case: englishMetadata?.useCase ?? template?.useCase,
template_license: englishMetadata?.license ?? template?.license,
share_id: shareId,
...nodeCounts
}
}
@@ -108,6 +113,7 @@ export function getExecutionContext(): ExecutionContext {
return {
is_template: false,
workflow_name: activeWorkflow.filename,
share_id: shareId,
...nodeCounts
}
}
@@ -115,6 +121,7 @@ export function getExecutionContext(): ExecutionContext {
return {
is_template: false,
workflow_name: undefined,
share_id: shareId,
...nodeCounts
}
}

View File

@@ -30,8 +30,9 @@ import { useAppMode } from '@/composables/useAppMode'
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
import { useTelemetry } from '@/platform/telemetry'
const { url } = defineProps<{
const { url, shareId } = defineProps<{
url: string
shareId: string
}>()
const { copyToClipboard } = useCopyToClipboard()
@@ -43,7 +44,8 @@ async function handleCopy() {
copied.value = true
useTelemetry()?.trackShareFlow({
step: 'link_copied',
source: isAppMode.value ? 'app_mode' : 'graph_mode'
source: isAppMode.value ? 'app_mode' : 'graph_mode',
share_id: shareId
})
}
</script>

View File

@@ -112,7 +112,10 @@
</template>
<template v-if="dialogState === 'shared' && publishResult">
<ShareUrlCopyField :url="publishResult.shareUrl" />
<ShareUrlCopyField
:url="publishResult.shareUrl"
:share-id="publishResult.shareId"
/>
<div class="flex flex-col gap-1">
<p
v-if="publishResult.publishedAt"
@@ -437,7 +440,8 @@ const {
acknowledged.value = false
useTelemetry()?.trackShareFlow({
step: 'link_created',
source: getShareSource()
source: getShareSource(),
share_id: result.shareId
})
return result

View File

@@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useSharedWorkflowUrlLoader } from '@/platform/workflow/sharing/composables/useSharedWorkflowUrlLoader'
import type { SharedWorkflowPayload } from '@/platform/workflow/sharing/types/shareTypes'
import { getShareProvenance } from '@/platform/workflow/sharing/utils/shareProvenance'
const preservedQueryMocks = vi.hoisted(() => ({
clearPreservedQuery: vi.fn(),
@@ -52,6 +53,37 @@ vi.mock('@/scripts/app', () => ({
}
}))
const mockTrackShareLinkOpened = vi.hoisted(() => vi.fn())
vi.mock('@/platform/telemetry', () => ({
useTelemetry: () => ({
trackShareLinkOpened: mockTrackShareLinkOpened
})
}))
const mockAuthState = vi.hoisted(() => ({
isAuthenticated: false,
currentUser: null as {
metadata: { creationTime?: string; lastSignInTime?: string }
} | null
}))
vi.mock('@/stores/authStore', () => ({
useAuthStore: () => mockAuthState
}))
const mockActiveWorkflow = vi.hoisted(() => ({
current: null as { path: string } | null
}))
vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
useWorkflowStore: () => ({
get activeWorkflow() {
return mockActiveWorkflow.current
}
})
}))
const mockToastAdd = vi.fn()
vi.mock('primevue/usetoast', () => ({
useToast: () => ({
@@ -174,6 +206,9 @@ describe('useSharedWorkflowUrlLoader', () => {
beforeEach(() => {
vi.resetAllMocks()
mockQueryParams = {}
mockAuthState.isAuthenticated = false
mockAuthState.currentUser = null
mockActiveWorkflow.current = null
mockDialogStack.length = 0
mockShowLayoutDialog.mockImplementation(createDialogInstance)
mockUpdateDialog.mockImplementation(
@@ -234,7 +269,7 @@ describe('useSharedWorkflowUrlLoader', () => {
true,
true,
'Test Workflow',
{ openSource: 'shared_url' }
{ openSource: 'shared_url', shareId: 'share-id-1' }
)
expect(mockRouterReplace).toHaveBeenCalledWith({ query: {} })
expect(preservedQueryMocks.clearPreservedQuery).toHaveBeenCalledWith(
@@ -411,7 +446,7 @@ describe('useSharedWorkflowUrlLoader', () => {
true,
true,
'Test Workflow',
{ openSource: 'shared_url' }
{ openSource: 'shared_url', shareId: 'share-id-1' }
)
expect(mockToastAdd).toHaveBeenCalledWith(
expect.objectContaining({
@@ -541,7 +576,93 @@ describe('useSharedWorkflowUrlLoader', () => {
true,
true,
'Open shared workflow',
{ openSource: 'shared_url' }
{ openSource: 'shared_url', shareId: 'share-id-1' }
)
})
it('tracks share_link_opened with auth state when share param is valid', async () => {
mockQueryParams = { share: 'share-id-1' }
mockAuthState.isAuthenticated = true
mockAuthState.currentUser = {
metadata: {
creationTime: 'Mon, 01 Jun 2026 12:00:00 GMT',
lastSignInTime: 'Mon, 08 Jun 2026 12:00:00 GMT'
}
}
mockShowLayoutDialog.mockImplementation(() => {
resolveDialogWithCancel()
})
const { loadSharedWorkflowFromUrl } = useSharedWorkflowUrlLoader()
await loadSharedWorkflowFromUrl()
expect(mockTrackShareLinkOpened).toHaveBeenCalledTimes(1)
expect(mockTrackShareLinkOpened).toHaveBeenCalledWith({
share_id: 'share-id-1',
is_authenticated: true,
is_new_user: false
})
})
it('tracks share_link_opened with is_new_user=true for a just-created account', async () => {
mockQueryParams = { share: 'share-id-1' }
mockAuthState.isAuthenticated = true
mockAuthState.currentUser = {
metadata: {
creationTime: 'Mon, 08 Jun 2026 12:00:00 GMT',
lastSignInTime: 'Mon, 08 Jun 2026 12:00:05 GMT'
}
}
mockShowLayoutDialog.mockImplementation(() => {
resolveDialogWithCancel()
})
const { loadSharedWorkflowFromUrl } = useSharedWorkflowUrlLoader()
await loadSharedWorkflowFromUrl()
expect(mockTrackShareLinkOpened).toHaveBeenCalledWith({
share_id: 'share-id-1',
is_authenticated: true,
is_new_user: true
})
})
it('omits is_new_user when unauthenticated', async () => {
mockQueryParams = { share: 'share-id-1' }
mockShowLayoutDialog.mockImplementation(() => {
resolveDialogWithCancel()
})
const { loadSharedWorkflowFromUrl } = useSharedWorkflowUrlLoader()
await loadSharedWorkflowFromUrl()
expect(mockTrackShareLinkOpened).toHaveBeenCalledWith({
share_id: 'share-id-1',
is_authenticated: false,
is_new_user: undefined
})
})
it('does not track share_link_opened for absent or invalid share params', async () => {
const { loadSharedWorkflowFromUrl } = useSharedWorkflowUrlLoader()
await loadSharedWorkflowFromUrl()
mockQueryParams = { share: '../../../etc/passwd' }
await loadSharedWorkflowFromUrl()
expect(mockTrackShareLinkOpened).not.toHaveBeenCalled()
})
it('records share provenance for the imported workflow', async () => {
mockQueryParams = { share: 'share-id-1' }
mockActiveWorkflow.current = { path: 'workflows/shared-test.json' }
mockShowLayoutDialog.mockImplementation(() => {
resolveDialogWithConfirm(makePayload())
})
const { loadSharedWorkflowFromUrl } = useSharedWorkflowUrlLoader()
await loadSharedWorkflowFromUrl()
expect(getShareProvenance('workflows/shared-test.json')).toBe('share-id-1')
})
})

View File

@@ -1,3 +1,4 @@
import type { User } from 'firebase/auth'
import { useToast } from 'primevue/usetoast'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
@@ -11,9 +12,13 @@ import {
mergePreservedQueryIntoQuery
} from '@/platform/navigation/preservedQueryManager'
import { PRESERVED_QUERY_NAMESPACES } from '@/platform/navigation/preservedQueryNamespaces'
import { useTelemetry } from '@/platform/telemetry'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
import { useWorkflowShareService } from '@/platform/workflow/sharing/services/workflowShareService'
import { recordShareProvenance } from '@/platform/workflow/sharing/utils/shareProvenance'
import { app } from '@/scripts/app'
import { useDialogService } from '@/services/dialogService'
import { useAuthStore } from '@/stores/authStore'
import { useDialogStore } from '@/stores/dialogStore'
type SharedWorkflowUrlLoadStatus =
@@ -47,6 +52,22 @@ export function useSharedWorkflowUrlLoader() {
return /^[a-zA-Z0-9_.-]+$/.test(param)
}
function isNewlyCreatedUser(user: User | null): boolean | undefined {
const creationTime = user?.metadata.creationTime
const lastSignInTime = user?.metadata.lastSignInTime
if (!creationTime || !lastSignInTime) return undefined
return Date.parse(lastSignInTime) - Date.parse(creationTime) < 60_000
}
function trackShareLinkOpened(shareId: string) {
const authStore = useAuthStore()
useTelemetry()?.trackShareLinkOpened({
share_id: shareId,
is_authenticated: authStore.isAuthenticated,
is_new_user: isNewlyCreatedUser(authStore.currentUser)
})
}
async function ensureShareQueryFromIntent() {
hydratePreservedQuery(SHARE_NAMESPACE)
const mergedQuery = mergePreservedQueryIntoQuery(
@@ -140,6 +161,8 @@ export function useSharedWorkflowUrlLoader() {
return 'failed'
}
trackShareLinkOpened(shareParam)
const result = await showOpenSharedWorkflowDialog(shareParam)
if (result.action === 'cancel') {
@@ -182,9 +205,14 @@ export function useSharedWorkflowUrlLoader() {
true,
workflowName,
{
openSource: 'shared_url'
openSource: 'shared_url',
shareId: payload.shareId
}
)
const workflowKey = useWorkflowStore().activeWorkflow?.path
if (workflowKey) {
recordShareProvenance(workflowKey, payload.shareId)
}
} catch (error) {
console.error(
'[useSharedWorkflowUrlLoader] Failed to load workflow graph:',

View File

@@ -0,0 +1,18 @@
/**
* Session-scoped record of which workflows were imported from a share link.
* Keyed by workflow path so telemetry (e.g. execution_start) can attribute
* runs of a shared workflow to the share_id that delivered it. Intentionally
* in-memory only: attribution does not survive a reload or a save/rename.
*/
const shareIdByWorkflowKey = new Map<string, string>()
export function recordShareProvenance(
workflowKey: string,
shareId: string
): void {
shareIdByWorkflowKey.set(workflowKey, shareId)
}
export function getShareProvenance(workflowKey: string): string | undefined {
return shareIdByWorkflowKey.get(workflowKey)
}

View File

@@ -1138,6 +1138,7 @@ export class ComfyApp {
options: {
checkForRerouteMigration?: boolean
openSource?: WorkflowOpenSource
shareId?: string
deferWarnings?: boolean
skipAssetScans?: boolean
silentAssetErrors?: boolean
@@ -1146,6 +1147,7 @@ export class ComfyApp {
const {
checkForRerouteMigration = false,
openSource,
shareId,
deferWarnings = false,
skipAssetScans = false,
silentAssetErrors = false
@@ -1429,7 +1431,8 @@ export class ComfyApp {
typeof node === 'string' ? node : node.type
),
missing_node_packs: groupMissingNodesByPack(missingNodeTypes),
open_source: openSource ?? 'unknown'
open_source: openSource ?? 'unknown',
share_id: shareId
}
useTelemetry()?.trackWorkflowOpened(telemetryPayload)
useTelemetry()?.trackWorkflowImported(telemetryPayload)

View File

@@ -3,9 +3,10 @@ import type { User, UserCredential } from 'firebase/auth'
import * as firebaseAuth from 'firebase/auth'
import { setActivePinia } from 'pinia'
import type { Mock } from 'vitest'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import * as vuefire from 'vuefire'
import { clearPreservedQuery } from '@/platform/navigation/preservedQueryManager'
import { useDialogService } from '@/services/dialogService'
import { useAuthStore } from '@/stores/authStore'
import { createTestingPinia } from '@pinia/testing'
@@ -582,6 +583,45 @@ describe('useAuthStore', () => {
expect(store.loading).toBe(false)
})
describe('share intent attribution', () => {
afterEach(() => {
sessionStorage.removeItem('Comfy.PreservedQuery.share')
clearPreservedQuery('share')
})
it('attaches preserved share_id to sign-up telemetry', async () => {
sessionStorage.setItem(
'Comfy.PreservedQuery.share',
JSON.stringify({ share: 'abc123def456' })
)
const mockUserCredential = { user: mockUser }
vi.mocked(
firebaseAuth.createUserWithEmailAndPassword
).mockResolvedValue(
mockUserCredential as Partial<UserCredential> as UserCredential
)
await store.register('new@example.com', 'password')
expect(mockTrackAuth).toHaveBeenCalledWith(
expect.objectContaining({ share_id: 'abc123def456' })
)
})
it('tracks no share_id when no share intent is preserved', async () => {
const mockUserCredential = { user: mockUser }
vi.mocked(firebaseAuth.signInWithEmailAndPassword).mockResolvedValue(
mockUserCredential as Partial<UserCredential> as UserCredential
)
await store.login('user@example.com', 'password')
expect(mockTrackAuth).toHaveBeenCalledWith(
expect.objectContaining({ share_id: undefined })
)
})
})
describe('sign-up telemetry OR logic', () => {
const mockUserCredential = {
user: mockUser

View File

@@ -23,6 +23,11 @@ import { useFirebaseAuth } from 'vuefire'
import { getComfyApiBaseUrl } from '@/config/comfyApi'
import { t } from '@/i18n'
import { isCloud } from '@/platform/distribution/types'
import {
getPreservedQueryParam,
hydratePreservedQuery
} from '@/platform/navigation/preservedQueryManager'
import { PRESERVED_QUERY_NAMESPACES } from '@/platform/navigation/preservedQueryNamespaces'
import { useTelemetry } from '@/platform/telemetry'
import { useDialogService } from '@/services/dialogService'
import { useWorkspaceAuthStore } from '@/platform/workspace/stores/workspaceAuthStore'
@@ -318,6 +323,11 @@ export const useAuthStore = defineStore('auth', () => {
}
}
const getShareIntentId = (): string | undefined => {
hydratePreservedQuery(PRESERVED_QUERY_NAMESPACES.SHARE)
return getPreservedQueryParam(PRESERVED_QUERY_NAMESPACES.SHARE, 'share')
}
const login = async (
email: string,
password: string
@@ -333,7 +343,8 @@ export const useAuthStore = defineStore('auth', () => {
method: 'email',
is_new_user: false,
user_id: result.user.uid,
email: result.user.email ?? undefined
email: result.user.email ?? undefined,
share_id: getShareIntentId()
})
}
@@ -355,7 +366,8 @@ export const useAuthStore = defineStore('auth', () => {
method: 'email',
is_new_user: true,
user_id: result.user.uid,
email: result.user.email ?? undefined
email: result.user.email ?? undefined,
share_id: getShareIntentId()
})
}
@@ -377,7 +389,8 @@ export const useAuthStore = defineStore('auth', () => {
is_new_user:
options?.isNewUser || additionalUserInfo?.isNewUser || false,
user_id: result.user.uid,
email: result.user.email ?? undefined
email: result.user.email ?? undefined,
share_id: getShareIntentId()
})
}
@@ -399,7 +412,8 @@ export const useAuthStore = defineStore('auth', () => {
is_new_user:
options?.isNewUser || additionalUserInfo?.isNewUser || false,
user_id: result.user.uid,
email: result.user.email ?? undefined
email: result.user.email ?? undefined,
share_id: getShareIntentId()
})
}