diff --git a/packages/tokens-studio-for-figma/src/app/components/ImportedTokensDialog.tsx b/packages/tokens-studio-for-figma/src/app/components/ImportedTokensDialog.tsx index 26776b3a6..e1b6f35e6 100644 --- a/packages/tokens-studio-for-figma/src/app/components/ImportedTokensDialog.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/ImportedTokensDialog.tsx @@ -14,6 +14,7 @@ import { ImportToken } from '@/types/tokens'; import Text from './Text'; import Accordion from './Accordion'; import { Count } from './Count'; +import { track } from '@/utils/analytics'; function NewOrExistingToken({ token, @@ -164,6 +165,27 @@ export default function ImportedTokensDialog() { // Update all existing tokens, and create new ones importMultipleTokens({ multipleUpdatedTokens, multipleNewTokens }); + const combinedTokens = [...multipleUpdatedTokens, ...multipleNewTokens]; + + const uniqueCollectionCount = combinedTokens.reduce((acc, token) => { + if (token.parent && !acc.includes(token.parent)) { + acc.push(token.parent); + } + return acc; + }, [] as string[]).length; + + track('Import variables', { + totalCount: multipleUpdatedTokens.length + multipleNewTokens.length, + updatedCount: multipleUpdatedTokens.length, + newCount: multipleNewTokens.length, + colorTokens: combinedTokens.filter((token) => token.type === 'color').length, + textTokens: combinedTokens.filter((token) => token.type === 'text').length, + numberTokens: combinedTokens.filter((token) => token.type === 'number').length, + booleanTokens: combinedTokens.filter((token) => token.type === 'boolean').length, + dimensionTokens: combinedTokens.filter((token) => token.type === 'dimension').length, + collectionCount: uniqueCollectionCount, + }); + setUpdatedTokens([]); setNewTokens([]); }, [activeTokenSet, importMultipleTokens, newTokens, updatedTokens]); @@ -178,6 +200,7 @@ export default function ImportedTokensDialog() { description: token.description, shouldUpdateDocument: false, }); + track('Import single variable', { type: token.type }); setNewTokens(newTokens.filter((newToken) => newToken.name !== token.name)); }, [newTokens, activeTokenSet, createSingleToken]); @@ -191,6 +214,8 @@ export default function ImportedTokensDialog() { description: token.description, shouldUpdateDocument: false, }); + track('Update single variable', { type: token.type }); + setUpdatedTokens(updatedTokens.filter((updatedToken) => updatedToken.name !== token.name)); }, [updatedTokens, editSingleToken, activeTokenSet]); diff --git a/packages/tokens-studio-for-figma/src/app/components/Initiator.tsx b/packages/tokens-studio-for-figma/src/app/components/Initiator.tsx index 9f50e6e92..2dd5af451 100644 --- a/packages/tokens-studio-for-figma/src/app/components/Initiator.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/Initiator.tsx @@ -105,7 +105,6 @@ export function Initiator() { case MessageFromPluginTypes.VARIABLES: { const { values } = pluginMessage; if (values) { - track('Import variables'); dispatch.tokenState.setTokensFromVariables(values); dispatch.uiState.setActiveTab(Tabs.TOKENS); } diff --git a/packages/tokens-studio-for-figma/src/app/store/remoteTokens.tsx b/packages/tokens-studio-for-figma/src/app/store/remoteTokens.tsx index 201b6f01a..ca553cddc 100644 --- a/packages/tokens-studio-for-figma/src/app/store/remoteTokens.tsx +++ b/packages/tokens-studio-for-figma/src/app/store/remoteTokens.tsx @@ -107,7 +107,6 @@ export default function useRemoteTokens() { async ({ context = api, featureFlags, usedTokenSet, activeTheme, collapsedTokenSets, updateLocalTokens = false, }: PullTokensOptions) => { - track('pullTokens', { provider: context.provider }); showPullDialog('loading'); let remoteData: RemoteResponseData | null = null; switch (context.provider) { @@ -241,6 +240,22 @@ export default function useRemoteTokens() { } } } + try { + if (remoteData?.status === 'success') { + const setCount = Object.keys(remoteData.tokens).length; + const tokensCount = Object.values(remoteData.tokens).reduce((acc, set) => acc + set.length, 0); + const themeCount = Object.keys(remoteData.themes).length; + const tokenFormat = getFormat(); + track('pullTokens', { + provider: context.provider, setCount, tokensCount, themeCount, tokenFormat, + }); + } else { + track('pullTokens failure', { provider: context.provider }); + } + } catch (e) { + console.error(e); + } + dispatch.tokenState.resetChangedState(); closePullDialog(); return remoteData; @@ -333,7 +348,6 @@ export default function useRemoteTokens() { const pushTokens = useCallback( async ({ context = api, overrides }: { context?: StorageTypeCredentials, overrides?: PushOverrides } = {}) => { const isFolder = 'filePath' in context && !context.filePath?.endsWith('.json'); - track('pushTokens', { provider: context.provider, isFolder }); let pushResult; switch (context.provider) { case StorageProviderType.GITHUB: { @@ -363,19 +377,26 @@ export default function useRemoteTokens() { default: throw new Error('Not implemented'); } + try { + if (pushResult?.status === 'success') { + const setCount = Object.keys(tokens).length; + const tokensCount = Object.values(tokens).reduce((acc, set) => acc + set.length, 0); + const themeCount = Object.keys(themes).length; + const tokenFormat = getFormat(); + track('pushTokens', { + provider: context.provider, isFolder, setCount, tokensCount, themeCount, tokenFormat, + }); + } else { + track('pushTokens failure', { provider: context.provider, isFolder }); + } + } catch (e) { + console.error(e); + } if (pushResult.status && pushResult.status === 'failure') { notifyToUI(pushResult.errorMessage, { error: true }); } }, - [ - api, - pushTokensToGitHub, - pushTokensToGitLab, - pushTokensToBitbucket, - pushTokensToADO, - pushTokensToSupernova, - pushTokensToTokensStudio, - ], + [api, pushTokensToGitHub, pushTokensToGitLab, pushTokensToBitbucket, pushTokensToADO, pushTokensToSupernova, pushTokensToTokensStudio, tokens, themes], ); const addNewProviderItem = useCallback( diff --git a/packages/tokens-studio-for-figma/src/app/store/updateSources.tsx b/packages/tokens-studio-for-figma/src/app/store/updateSources.tsx index 813d8b425..95e7591c6 100644 --- a/packages/tokens-studio-for-figma/src/app/store/updateSources.tsx +++ b/packages/tokens-studio-for-figma/src/app/store/updateSources.tsx @@ -13,7 +13,7 @@ import { AsyncMessageChannel } from '@/AsyncMessageChannel'; import { StorageProviderType } from '@/constants/StorageProviderType'; import { StorageType, StorageTypeCredentials } from '@/types/StorageType'; import { defaultTokenResolver } from '@/utils/TokenResolver'; -import { TokenFormatOptions } from '@/plugin/TokenFormatStoreClass'; +import { getFormat, TokenFormatOptions } from '@/plugin/TokenFormatStoreClass'; type UpdateRemoteTokensPayload = { provider: StorageProviderType; @@ -59,10 +59,12 @@ async function updateRemoteTokens({ dispatch, }: UpdateRemoteTokensPayload) { if (!context) return; + const setCount = Object.keys(tokens)?.length; + const tokensCount = Object.values(tokens).reduce((acc, set) => acc + set.length, 0); + const themeCount = Object.keys(themes).length; + const tokenFormat = getFormat(); switch (provider) { case StorageProviderType.JSONBIN: { - track('pushTokens', { provider: StorageProviderType.JSONBIN }); - notifyToUI('Updating JSONBin...'); await updateJSONBinTokens({ themes, @@ -73,10 +75,13 @@ async function updateRemoteTokens({ storeTokenIdInJsonEditor, dispatch, }); + track('pushTokens', { + provider: StorageProviderType.JSONBIN, setCount, tokensCount, themeCount, tokenFormat, + }); + break; } case StorageProviderType.GENERIC_VERSIONED_STORAGE: { - track('pushTokens', { provider: StorageProviderType.GENERIC_VERSIONED_STORAGE }); notifyToUI('Updating Generic Remote...'); await updateGenericVersionedTokens({ themes, @@ -88,6 +93,10 @@ async function updateRemoteTokens({ dispatch, }); + track('pushTokens', { + provider: StorageProviderType.GENERIC_VERSIONED_STORAGE, setCount, tokensCount, themeCount, tokenFormat, + }); + break; } case StorageProviderType.GITHUB: { diff --git a/packages/tokens-studio-for-figma/src/app/store/useTokens.tsx b/packages/tokens-studio-for-figma/src/app/store/useTokens.tsx index 90345082f..416f80747 100644 --- a/packages/tokens-studio-for-figma/src/app/store/useTokens.tsx +++ b/packages/tokens-studio-for-figma/src/app/store/useTokens.tsx @@ -189,7 +189,7 @@ export default function useTokens() { const pullVariables = useCallback(async () => { const userDecision = await confirm({ - text: 'Import Variables', + text: 'Import variables', description: 'Sets will be created for each variable mode.', choices: [ { key: 'useDimensions', label: 'Convert numbers to dimensions', enabled: false }, @@ -199,7 +199,6 @@ export default function useTokens() { }); if (userDecision) { - track('Import variables'); AsyncMessageChannel.ReactInstance.message({ type: AsyncMessageTypes.PULL_VARIABLES, options: { @@ -370,13 +369,6 @@ export default function useTokens() { const shouldCreateStyles = (settings.stylesTypography || settings.stylesColor || settings.stylesEffect) && selectedSets.length > 0; if (!shouldCreateStyles) return; - track('createStyles', { - type: 'sets', - textStyles: settings.stylesTypography, - colorStyles: settings.stylesColor, - effectStyles: settings.stylesEffect, - }); - dispatch.uiState.startJob({ name: BackgroundJobs.UI_CREATE_STYLES, isInfinite: true, @@ -422,6 +414,20 @@ export default function useTokens() { settings, })); + track('createStyles', { + type: 'sets', + textStyles: settings.stylesTypography, + colorStyles: settings.stylesColor, + effectStyles: settings.stylesEffect, + setCount: selectedSets.length, + totalStyles: tokensToCreate.length, + removeStylesAndVariablesWithoutConnection: settings.removeStylesAndVariablesWithoutConnection, + renameExistingStylesAndVariables: settings.renameExistingStylesAndVariables, + ignoreFirstPartForStyles: settings.ignoreFirstPartForStyles, + prefixStylesWithThemeName: settings.prefixStylesWithThemeName, + createStylesWithVariableReferences: settings.createStylesWithVariableReferences, + }); + dispatch.uiState.completeJob(BackgroundJobs.UI_CREATE_STYLES); }, [tokens, settings, dispatch.uiState], @@ -432,18 +438,12 @@ export default function useTokens() { const shouldCreateStyles = (settings.stylesTypography || settings.stylesColor || settings.stylesEffect) && selectedThemes.length > 0; if (!shouldCreateStyles) return; - track('createStyles', { - type: 'themes', - textStyles: settings.stylesTypography, - colorStyles: settings.stylesColor, - effectStyles: settings.stylesEffect, - }); - dispatch.uiState.startJob({ name: BackgroundJobs.UI_CREATE_STYLES, isInfinite: true, }); + let totalTokensToCreate = 0; // Iterate over all given selectedThemes, and combine the selectedTokenSets. const overallConfig = getOverallConfig(themes, selectedThemes); @@ -488,6 +488,8 @@ export default function useTokens() { return acc; }, []); + totalTokensToCreate += tokensToCreate.length; + const createStylesResult = await wrapTransaction({ name: 'createStyles' }, async () => AsyncMessageChannel.ReactInstance.message({ type: AsyncMessageTypes.CREATE_STYLES, tokens: tokensToCreate, @@ -505,6 +507,19 @@ export default function useTokens() { } } + track('createStyles', { + type: 'themes', + textStyles: settings.stylesTypography, + colorStyles: settings.stylesColor, + effectStyles: settings.stylesEffect, + themesCount: selectedThemes.length, + totalTokens: totalTokensToCreate, + removeStylesAndVariablesWithoutConnection: settings.removeStylesAndVariablesWithoutConnection, + renameExistingStylesAndVariables: settings.renameExistingStylesAndVariables, + ignoreFirstPartForStyles: settings.ignoreFirstPartForStyles, + prefixStylesWithThemeName: settings.prefixStylesWithThemeName, + createStylesWithVariableReferences: settings.createStylesWithVariableReferences, + }); // Remove styles that aren't in the theme or in the exposed token object if (settings.removeStylesAndVariablesWithoutConnection) { const uniqueMergedStyleIds: string[] = Array.from(new Set([ @@ -637,9 +652,6 @@ export default function useTokens() { && selectedSets.length > 0; if (!shouldCreateVariables) return; - track('createVariables', { - type: 'sets', - }); dispatch.uiState.startJob({ name: BackgroundJobs.UI_CREATEVARIABLES, isInfinite: true, @@ -650,6 +662,17 @@ export default function useTokens() { statExtractor: async (result, transaction) => { const data = await result; if (data) { + track('createVariables', { + type: 'sets', + totalVariables: data.totalVariables, + setCount: selectedSets.length, + variablesColor: settings.variablesColor, + variablesNumber: settings.variablesNumber, + variablesString: settings.variablesString, + variablesBoolean: settings.variablesBoolean, + removeStylesAndVariablesWithoutConnection: settings.removeStylesAndVariablesWithoutConnection, + renameExistingStylesAndVariables: settings.renameExistingStylesAndVariables, + }); transaction.setMeasurement('variables', data.totalVariables, ''); } }, @@ -676,10 +699,6 @@ export default function useTokens() { && selectedThemes.length > 0; if (!shouldCreateVariables) return; - track('createVariables', { - type: 'themes', - }); - dispatch.uiState.startJob({ name: BackgroundJobs.UI_CREATEVARIABLES, isInfinite: true, @@ -690,6 +709,17 @@ export default function useTokens() { statExtractor: async (result, transaction) => { const data = await result; if (data) { + track('createVariables', { + type: 'themes', + totalVariables: data.totalVariables, + themeCount: selectedThemes.length, + variablesColor: settings.variablesColor, + variablesNumber: settings.variablesNumber, + variablesString: settings.variablesString, + variablesBoolean: settings.variablesBoolean, + removeStylesAndVariablesWithoutConnection: settings.removeStylesAndVariablesWithoutConnection, + renameExistingStylesAndVariables: settings.renameExistingStylesAndVariables, + }); transaction.setMeasurement('variables', data.totalVariables, ''); } }, diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/en/tokens.json b/packages/tokens-studio-for-figma/src/i18n/lang/en/tokens.json index 16efcbd28..104e88be2 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/en/tokens.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/en/tokens.json @@ -88,9 +88,9 @@ "styles": "Styles", "stylesAndVariables": "Styles & Variables", "syncStyles": "Sync styles", - "exportStylesAndVariables": "Export styles & variables", + "exportStylesAndVariables": "Export styles & variables to Figma", "import": "Import", - "importStyles": "Import Styles", + "importStyles": "Import styles", "createStyles": "Create styles", "importVariables": "Import variables", "createVariables": "Create variables",