Skip to content

Commit 9122ca9

Browse files
authored
feat: common actions analytics [INS-2120] (Kong#9664)
* feat: common actions analytics [INS-2120]
1 parent b00638b commit 9122ca9

23 files changed

+319
-68
lines changed

packages/insomnia/src/routes/organization.$organizationId.project.$projectId._index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import { useInsomniaSyncPullRemoteFileActionFetcher } from '~/routes/organizatio
5757
import { useWorkspaceNewActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.new';
5858
import { useStorageRulesLoaderFetcher } from '~/routes/organization.$organizationId.storage-rules';
5959
import { VCSInstance } from '~/sync/vcs/insomnia-sync';
60-
import { SegmentEvent } from '~/ui/analytics';
60+
import { SegmentEvent, trackOnceDaily } from '~/ui/analytics';
6161
import { AvatarGroup } from '~/ui/components/avatar';
6262
import { CloudSyncProjectBar } from '~/ui/components/dropdowns/cloud-sync-project-bar';
6363
import { GitProjectSyncDropdown } from '~/ui/components/dropdowns/git-project-sync-dropdown';
@@ -950,7 +950,12 @@ const Component = () => {
950950
aria-label="Files filter"
951951
className="group relative flex-1"
952952
value={workspaceListFilter}
953-
onChange={filter => setWorkspaceListFilter(filter)}
953+
onChange={filter => {
954+
setWorkspaceListFilter(filter);
955+
if (filter.trim() !== '') {
956+
trackOnceDaily(SegmentEvent.homepageFiltered);
957+
}
958+
}}
954959
>
955960
<Input
956961
placeholder="Filter"

packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.tsx

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -848,12 +848,24 @@ const Debug = () => {
848848
<div className="flex w-full items-center justify-between gap-2">
849849
<EnvironmentPicker
850850
isOpen={isEnvironmentPickerOpen}
851-
onOpenChange={setIsEnvironmentPickerOpen}
851+
onOpenChange={isOpen => {
852+
setIsEnvironmentPickerOpen(isOpen);
853+
if (isOpen) {
854+
window.main.trackSegmentEvent({
855+
event: SegmentEvent.requestEnvironmentClicked,
856+
});
857+
}
858+
}}
852859
onOpenEnvironmentSettingsModal={() => setEnvironmentModalOpen(true)}
853860
/>
854861
</div>
855862
<Button
856-
onPress={() => setIsCookieModalOpen(true)}
863+
onPress={() => {
864+
window.main.trackSegmentEvent({
865+
event: SegmentEvent.requestAddCookiesClicked,
866+
});
867+
setIsCookieModalOpen(true);
868+
}}
857869
className="flex max-w-full flex-1 items-center justify-center gap-2 truncate rounded-xs px-4 py-1 text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
858870
>
859871
<Icon icon="cookie-bite" className="w-5 shrink-0" />
@@ -863,7 +875,12 @@ const Debug = () => {
863875
</span>
864876
</Button>
865877
<Button
866-
onPress={() => setCertificatesModalOpen(true)}
878+
onPress={() => {
879+
window.main.trackSegmentEvent({
880+
event: SegmentEvent.requestAddCertificatesClicked,
881+
});
882+
setCertificatesModalOpen(true);
883+
}}
867884
className="flex max-w-full flex-1 items-center justify-center gap-2 truncate rounded-xs px-4 py-1 text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
868885
>
869886
<Icon icon="file-contract" className="w-5 shrink-0" />
@@ -907,13 +924,17 @@ const Debug = () => {
907924
aria-label="Sort order"
908925
className="aspect-square h-full"
909926
selectedKey={sortOrder}
910-
onSelectionChange={order =>
911-
order &&
912-
setSearchParams({
913-
...Object.fromEntries(searchParams.entries()),
914-
sortOrder: order.toString(),
915-
})
916-
}
927+
onSelectionChange={order => {
928+
if (order) {
929+
window.main.trackSegmentEvent({
930+
event: SegmentEvent.requestListSortClicked,
931+
});
932+
setSearchParams({
933+
...Object.fromEntries(searchParams.entries()),
934+
sortOrder: order.toString(),
935+
});
936+
}
937+
}}
917938
>
918939
<Button
919940
aria-label="Select sort order"
@@ -958,6 +979,9 @@ const Debug = () => {
958979
defaultSelected={allExpanded}
959980
onChange={() => {
960981
setAllExpanded(!allExpanded);
982+
window.main.trackSegmentEvent({
983+
event: SegmentEvent.requestListExpandCollapseClicked,
984+
});
961985
toggleExpandAllFetcher.submit({
962986
organizationId,
963987
projectId,

packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.spec.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { useWorkspaceLoaderData } from '~/routes/organization.$organizationId.pr
3939
import { useSpecGenerateRequestCollectionActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.spec.generate-request-collection';
4040
import { useSpecUpdateActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.spec.update';
4141
import { useStorageRulesLoaderFetcher } from '~/routes/organization.$organizationId.storage-rules';
42+
import { SegmentEvent } from '~/ui/analytics';
4243
import { CodeEditor, type CodeEditorHandle } from '~/ui/components/.client/codemirror/code-editor';
4344
import { DesignEmptyState } from '~/ui/components/design-empty-state';
4445
import { DocumentTab } from '~/ui/components/document-tab';
@@ -370,7 +371,15 @@ const Component = ({ params }: Route.ComponentProps) => {
370371
id: 'toggle-preview',
371372
name: 'Toggle preview',
372373
icon: <Icon className="w-3" icon={isSpecPaneOpen ? 'eye' : 'eye-slash'} />,
373-
action: () => setIsSpecPaneOpen(!isSpecPaneOpen),
374+
action: () => {
375+
window.main.trackSegmentEvent({
376+
event: SegmentEvent.designerPreviewToggled,
377+
properties: {
378+
status: !isSpecPaneOpen ? 'open' : 'collapsed',
379+
},
380+
});
381+
setIsSpecPaneOpen(!isSpecPaneOpen);
382+
},
374383
},
375384
];
376385

@@ -481,7 +490,12 @@ const Component = ({ params }: Route.ComponentProps) => {
481490
<span className="flex-1" />
482491
{isGenerateMockServersWithAIEnabled && (
483492
<Button
484-
onPress={() => setNewMockServerModalOpen(true)}
493+
onPress={() => {
494+
window.main.trackSegmentEvent({
495+
event: SegmentEvent.designerGenerateMockClicked,
496+
});
497+
setNewMockServerModalOpen(true);
498+
}}
485499
isDisabled={!apiSpec.contents}
486500
className="flex max-w-full flex-1 items-center justify-center gap-2 truncate rounded-xs px-4 py-1 text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset disabled:cursor-not-allowed disabled:opacity-50 aria-pressed:bg-(--hl-sm)"
487501
>
@@ -493,7 +507,15 @@ const Component = ({ params }: Route.ComponentProps) => {
493507
aria-label="Toggle preview"
494508
isSelected={isSpecPaneOpen}
495509
className="flex h-full items-center justify-center gap-2 rounded-xs px-2 text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
496-
onChange={setIsSpecPaneOpen}
510+
onChange={value => {
511+
setIsSpecPaneOpen(value);
512+
window.main.trackSegmentEvent({
513+
event: SegmentEvent.designerPreviewToggled,
514+
properties: {
515+
status: !value ? 'open' : 'collapsed',
516+
},
517+
});
518+
}}
497519
>
498520
{({ isSelected }) => (
499521
<>

packages/insomnia/src/routes/organization.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { useRootLoaderData } from '~/root';
2323
import { useWorkspaceLoaderData } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId';
2424
import { useSyncOrganizationsAndProjectsActionFetcher } from '~/routes/organization.sync-organizations-and-projects';
2525
import { useUntrackedProjectsLoaderFetcher } from '~/routes/untracked-projects';
26+
import { SegmentEvent } from '~/ui/analytics';
2627
import { getLoginUrl } from '~/ui/auth-session-provider.client';
2728
import { CommandPalette } from '~/ui/components/command-palette';
2829
import { GitHubStarsButton } from '~/ui/components/github-stars-button';
@@ -418,7 +419,15 @@ const Component = ({ loaderData }: Route.ComponentProps) => {
418419
<TooltipTrigger>
419420
<ToggleButton
420421
className="h-[10px] w-[10px] grow-0 gap-2 text-xs text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset"
421-
onChange={setIsOrganizationSidebarOpen}
422+
onChange={value => {
423+
setIsOrganizationSidebarOpen(value);
424+
window.main.trackSegmentEvent({
425+
event: SegmentEvent.statusbarLeftbarToggled,
426+
properties: {
427+
status: value ? 'open' : 'collapsed',
428+
},
429+
});
430+
}}
422431
isSelected={isOrganizationSidebarOpen}
423432
>
424433
{({ isSelected }) => {
@@ -456,6 +465,12 @@ const Component = ({ loaderData }: Route.ComponentProps) => {
456465
className="h-[10px] w-[10px] grow-0 rotate-90 gap-2 text-xs text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset"
457466
onChange={flag => {
458467
setIsMinimal(!flag);
468+
window.main.trackSegmentEvent({
469+
event: SegmentEvent.statusbarTopbarToggled,
470+
properties: {
471+
status: !flag ? 'minimal' : 'expanded',
472+
},
473+
});
459474
}}
460475
isSelected={!isMinimal}
461476
>
@@ -513,7 +528,12 @@ const Component = ({ loaderData }: Route.ComponentProps) => {
513528
<div>
514529
<Button
515530
className="flex h-full items-center justify-center gap-2 px-4 py-1 text-xs text-(--color-warning) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
516-
onPress={() => showModal(SettingsModal, { tab: 'data' })}
531+
onPress={() => {
532+
window.main.trackSegmentEvent({
533+
event: SegmentEvent.statusbarOrphanedProjectsClicked,
534+
});
535+
showModal(SettingsModal, { tab: 'data' });
536+
}}
517537
>
518538
<Icon icon="exclamation-circle" /> We have detected orphaned projects on your computer, click
519539
here to view them.
@@ -524,7 +544,12 @@ const Component = ({ loaderData }: Route.ComponentProps) => {
524544
<TooltipTrigger delay={500}>
525545
<Button
526546
className="flex h-full items-center justify-center gap-2 px-4 py-1 text-xs text-(--color-warning) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
527-
onPress={() => showModal(SettingsModal, { tab: 'data' })}
547+
onPress={() => {
548+
window.main.trackSegmentEvent({
549+
event: SegmentEvent.statusbarOrphanedProjectsClicked,
550+
});
551+
showModal(SettingsModal, { tab: 'data' });
552+
}}
528553
>
529554
<Icon icon="exclamation-circle" />
530555
</Button>

packages/insomnia/src/ui/analytics.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,48 @@ export enum SegmentEvent {
7676
// TODO(INS-1912): Remove in 12.5
7777
tempOrganizationOpened = 'temp_organization_opened',
7878
tempProjectOpened = 'temp_project_opened',
79+
80+
// INS-2120: Segment events to track common actions
81+
homepageFiltered = 'homepage-filtered',
82+
quickSearchOpenedByKeyboard = 'quick-search-opened-by-keyboard',
83+
quickSearchOpenedByMouse = 'quick-search-opened-by-mouse',
84+
statusbarLeftbarToggled = 'statusbar-leftbar-toggled',
85+
statusbarTopbarToggled = 'statusbar-topbar-toggled',
86+
statusbarOrphanedProjectsClicked = 'statusbar-orphaned-projects-clicked',
87+
designerGenerateMockClicked = 'designer-generate-mock-clicked',
88+
designerPreviewToggled = 'designer-preview-toggled',
89+
requestEnvironmentClicked = 'request-environment-clicked',
90+
requestAddCookiesClicked = 'request-add-cookies-clicked',
91+
requestAddCertificatesClicked = 'request-add-certificates-clicked',
92+
requestListSortClicked = 'request-list-sort-clicked',
93+
requestListExpandCollapseClicked = 'request-list-expand-collapse-clicked',
94+
requestParamsDescriptionToggled = 'request-params-description-toggled',
95+
requestParamsImportFromURLClicked = 'request-params-import-from-URL-clicked',
96+
requestParamsBulkEditToggled = 'request-params-bulk-edit-toggled',
97+
responsePreviewJSONPathEntered = 'response-preview-jsonpath-entered',
98+
requestBodyBeautifyClicked = 'request-body-beautify-clicked',
99+
requestHeadersDescriptionToggled = 'request-headers-description-toggled',
100+
requestHeadersBulkEditToggled = 'request-headers-bulk-edit-toggled',
101+
requestScriptsPreScriptSnippetAdded = 'request-scripts-prescript-snippet-added',
102+
requestScriptsPostScriptSnippetAdded = 'request-scripts-postscript-snippet-added',
103+
responseHeadersCopyAllClicked = 'response-headers-copy-all-clicked',
104+
responseCookiesManageCookiesClicked = 'response-cookies-manage-cookies-clicked',
105+
requestOpenInNewTabClicked = 'request-open-in-new-tab-clicked',
106+
requestListMenuPinClicked = 'request-list-menu-pin-clicked',
107+
requestListMenuDuplicateClicked = 'request-list-menu-duplicate-clicked',
108+
requestListMenuRenameClicked = 'request-list-menu-rename-clicked',
109+
requestListMenuSettingsClicked = 'request-list-menu-settings-clicked',
110+
requestSendMenuGenerateCodeClicked = 'request-send-menu-generate-code-clicked',
111+
requestSendMenuSendAfterDelayClicked = 'request-send-menu-send-after-delay-clicked',
112+
requestSendMenuRepeatAfterIntervalClicked = 'request-send-menu-repeat-after-interval-clicked',
113+
requestSendMenuDownloadAfterSendClicked = 'request-send-menu-download-after-send-clicked',
114+
requestSendMenuSendAndDownloadClicked = 'request-send-menu-send-and-download-clicked',
115+
mcpListExpandCollapseClicked = 'mcp-list-expand-collapse-clicked',
116+
mcpListFiltered = 'mcp-list-filtered',
117+
mcpRequestParamsBeautifyClicked = 'mcp-request-params-beautify-clicked',
118+
mcpRequestHeadersDescriptionToggled = 'mcp-request-headers-description-toggled',
119+
mcpRequestRootsNotifyClicked = 'mcp-request-roots-notify-clicked',
120+
mcpResponseHeadersCopyAllClicked = 'mcp-response-headers-copy-all-clicked',
79121
}
80122

81123
type PushPull = 'push' | 'pull';
@@ -99,3 +141,24 @@ type VCSAction =
99141
export function vcsSegmentEventProperties(type: 'git', action: VCSAction, error?: string) {
100142
return { type, action, error };
101143
}
144+
145+
function getTodayDateString(): string {
146+
return new Date().toISOString().split('T')[0];
147+
}
148+
149+
export function hasTrackedToday(key: string): boolean {
150+
const lastTracked = localStorage.getItem(key);
151+
return lastTracked === getTodayDateString();
152+
}
153+
154+
export function markTrackedToday(key: string): void {
155+
localStorage.setItem(key, getTodayDateString());
156+
}
157+
158+
export function trackOnceDaily(event: SegmentEvent, properties?: Record<string, unknown>): void {
159+
if (hasTrackedToday(event)) {
160+
return;
161+
}
162+
window.main.trackSegmentEvent({ event, properties });
163+
markTrackedToday(event);
164+
}

packages/insomnia/src/ui/components/.client/codemirror/code-editor.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { useRootLoaderData } from '~/root';
2525
import { getTagDefinitions } from '~/templating/index';
2626
import { type NunjucksParsedTag, type nunjucksTagContextMenuOptions } from '~/templating/types';
2727
import { extractNunjucksTagFromCoords } from '~/templating/utils';
28+
import { SegmentEvent, trackOnceDaily } from '~/ui/analytics';
2829
import { Icon } from '~/ui/components/icon';
2930
import { createKeybindingsHandler, useDocBodyKeyboardShortcuts } from '~/ui/components/keydown-binder';
3031
import { FilterHelpModal } from '~/ui/components/modals/filter-help-modal';
@@ -109,6 +110,7 @@ export interface CodeEditorProps {
109110
onChange?: (value: string, changeObj: EditorChange[]) => void;
110111
onCursorActivity?: (doc: CodeMirror.Editor) => void;
111112
onPaste?: (value: string) => string;
113+
onPrettify?: () => void;
112114
onClickLink?: CodeMirrorLinkClickCallback;
113115
pinToBottom?: boolean;
114116
placeholder?: string;
@@ -185,6 +187,7 @@ export const CodeEditor = memo(
185187
onChange,
186188
onCursorActivity,
187189
onPaste,
190+
onPrettify,
188191
onClickLink,
189192
pinToBottom,
190193
placeholder,
@@ -788,6 +791,9 @@ export const CodeEditor = memo(
788791
title="Filter response body"
789792
defaultValue={filter || ''}
790793
placeholder={mode?.includes('json') ? '$.store.books[*].author' : '/store/books/author'}
794+
onFocus={() => {
795+
trackOnceDaily(SegmentEvent.responsePreviewJSONPathEntered);
796+
}}
791797
onKeyDown={createKeybindingsHandler({
792798
Enter: () => {
793799
const filter = inputRef.current?.value;
@@ -861,6 +867,7 @@ export const CodeEditor = memo(
861867
if (mode?.includes('json') || mode?.includes('xml')) {
862868
maybePrettifyAndSetValue(codeMirror.current?.getValue(), true);
863869
}
870+
onPrettify?.();
864871
}}
865872
>
866873
Beautify {mode?.includes('json') ? 'JSON' : mode?.includes('xml') ? 'XML' : ''}

packages/insomnia/src/ui/components/command-palette.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { useCommandsLoaderFetcher } from '~/routes/commands';
3131
import { useInsomniaSyncPullRemoteFileActionFetcher } from '~/routes/organization.$organizationId.insomnia-sync.pull-remote-file';
3232
import { useSetActiveEnvironmentFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.environment.set-active';
3333
import { useRemoteFilesLoaderFetcher } from '~/routes/remote-files';
34+
import { SegmentEvent } from '~/ui/analytics';
3435
import { AvatarGroup } from '~/ui/components/avatar';
3536
import { Icon } from '~/ui/components/icon';
3637
import { useDocBodyKeyboardShortcuts } from '~/ui/components/keydown-binder';
@@ -48,13 +49,26 @@ export const CommandPalette = memo(function CommandPalette({ style = {} }: { sty
4849
useDocBodyKeyboardShortcuts({
4950
request_quickSwitch: () => {
5051
setIsOpen(true);
52+
window.main.trackSegmentEvent({
53+
event: SegmentEvent.quickSearchOpenedByKeyboard,
54+
});
5155
},
5256
});
5357

5458
const requestSwitchKeyCombination = getPlatformKeyCombinations(settings.hotKeyRegistry.request_quickSwitch)[0];
5559

5660
return (
57-
<DialogTrigger onOpenChange={setIsOpen} isOpen={isOpen}>
61+
<DialogTrigger
62+
onOpenChange={isOpen => {
63+
setIsOpen(isOpen);
64+
if (isOpen) {
65+
window.main.trackSegmentEvent({
66+
event: SegmentEvent.quickSearchOpenedByMouse,
67+
});
68+
}
69+
}}
70+
isOpen={isOpen}
71+
>
5872
<Button
5973
style={{ ...style }}
6074
data-testid="quick-search"

0 commit comments

Comments
 (0)