From a793ece1a549b008e4667b3e67edc58d181840da Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Tue, 7 Nov 2023 09:53:59 -0800 Subject: [PATCH] Add basic analytics --- src-tauri/src/main.rs | 2 +- src-tauri/src/models.rs | 9 ++++ src-web/components/GlobalHooks.tsx | 25 ++++++----- src-web/components/WorkspaceHeader.tsx | 2 +- src-web/hooks/useCreateEnvironment.ts | 4 +- src-web/hooks/useCreateFolder.ts | 2 + src-web/hooks/useCreateRequest.ts | 2 + src-web/hooks/useCreateWorkspace.ts | 2 + src-web/hooks/useDeleteAnyRequest.tsx | 2 + src-web/hooks/useDeleteEnvironment.tsx | 7 ++-- src-web/hooks/useDeleteFolder.tsx | 2 + src-web/hooks/useDeleteRequest.tsx | 37 ++-------------- src-web/hooks/useDeleteResponse.ts | 2 + src-web/hooks/useDeleteResponses.ts | 2 + src-web/hooks/useDeleteWorkspace.tsx | 2 + src-web/hooks/useDuplicateRequest.ts | 4 +- src-web/hooks/useIntrospectGraphQL.ts | 4 +- src-web/hooks/useSendAnyRequest.ts | 2 + src-web/hooks/useUpdateAnyRequest.ts | 14 +++++-- src-web/hooks/useUpdateRequest.ts | 27 ++---------- src-web/lib/analytics.ts | 58 ++++++++++++++++++++++++++ 21 files changed, 133 insertions(+), 78 deletions(-) create mode 100644 src-web/lib/analytics.ts diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 8cb35f8e..b80bd988 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -74,7 +74,7 @@ async fn send_ephemeral_request( db_instance: State<'_, Mutex>>, ) -> Result { let pool = &*db_instance.lock().await; - let response = models::HttpResponse::default(); + let response = models::HttpResponse::new(); let environment_id2 = environment_id.unwrap_or("n/a").to_string(); request.id = "".to_string(); return actually_send_request(request, &response, &environment_id2, &app_handle, pool).await; diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs index 5eef7072..59824a4c 100644 --- a/src-tauri/src/models.rs +++ b/src-tauri/src/models.rs @@ -118,6 +118,15 @@ pub struct HttpResponse { pub headers: Json>, } +impl HttpResponse { + pub(crate) fn new() -> Self { + Self { + model: "http_response".to_string(), + ..Default::default() + } + } +} + #[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] #[serde(default, rename_all = "camelCase")] pub struct KeyValue { diff --git a/src-web/components/GlobalHooks.tsx b/src-web/components/GlobalHooks.tsx index 6086e18e..fca2c7aa 100644 --- a/src-web/components/GlobalHooks.tsx +++ b/src-web/components/GlobalHooks.tsx @@ -1,20 +1,22 @@ import { useQueryClient } from '@tanstack/react-query'; import { appWindow } from '@tauri-apps/api/window'; +import { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; +import { useEffectOnce } from 'react-use'; import { keyValueQueryKey } from '../hooks/useKeyValue'; +import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; +import { useRecentEnvironments } from '../hooks/useRecentEnvironments'; +import { useRecentRequests } from '../hooks/useRecentRequests'; +import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces'; import { requestsQueryKey } from '../hooks/useRequests'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { responsesQueryKey } from '../hooks/useResponses'; -import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { workspacesQueryKey } from '../hooks/useWorkspaces'; +import { trackPage } from '../lib/analytics'; import { DEFAULT_FONT_SIZE } from '../lib/constants'; import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore'; import type { HttpRequest, HttpResponse, Model, Workspace } from '../lib/models'; import { modelsEq } from '../lib/models'; -import { useRecentRequests } from '../hooks/useRecentRequests'; -import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces'; -import { useRecentEnvironments } from '../hooks/useRecentEnvironments'; -import { useLocation } from 'react-router-dom'; -import { useEffect } from 'react'; import { setPathname } from '../lib/persistPathname'; export function GlobalHooks() { @@ -30,9 +32,13 @@ export function GlobalHooks() { // Listen for location changes and update the pathname const location = useLocation(); useEffect(() => { - setPathname(location.pathname); + setPathname(location.pathname).catch(console.error); }, [location.pathname]); + useEffectOnce(() => { + trackPage('/'); + }); + useListenToTauriEvent('created_model', ({ payload, windowLabel }) => { if (shouldIgnoreEvent(payload, windowLabel)) return; @@ -82,9 +88,8 @@ export function GlobalHooks() { } if (!shouldIgnoreModel(payload)) { - queryClient.setQueryData( - queryKey, - (values) => values?.map((v) => (modelsEq(v, payload) ? payload : v)), + queryClient.setQueryData(queryKey, (values) => + values?.map((v) => (modelsEq(v, payload) ? payload : v)), ); } }); diff --git a/src-web/components/WorkspaceHeader.tsx b/src-web/components/WorkspaceHeader.tsx index 91cd708a..6ba66d41 100644 --- a/src-web/components/WorkspaceHeader.tsx +++ b/src-web/components/WorkspaceHeader.tsx @@ -30,7 +30,7 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop +
{activeWorkspace?.name[0]?.toUpperCase()}
} diff --git a/src-web/hooks/useCreateEnvironment.ts b/src-web/hooks/useCreateEnvironment.ts index d1d46280..568d0678 100644 --- a/src-web/hooks/useCreateEnvironment.ts +++ b/src-web/hooks/useCreateEnvironment.ts @@ -1,9 +1,10 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; +import { trackEvent } from '../lib/analytics'; import type { Environment } from '../lib/models'; -import { environmentsQueryKey, useEnvironments } from './useEnvironments'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { useAppRoutes } from './useAppRoutes'; +import { environmentsQueryKey, useEnvironments } from './useEnvironments'; import { usePrompt } from './usePrompt'; import { useWorkspaces } from './useWorkspaces'; @@ -29,6 +30,7 @@ export function useCreateEnvironment() { : []; return invoke('create_environment', { name, variables, workspaceId }); }, + onSettled: () => trackEvent('environment', 'create'), onSuccess: async (environment) => { if (workspaceId == null) return; routes.setEnvironment(environment); diff --git a/src-web/hooks/useCreateFolder.ts b/src-web/hooks/useCreateFolder.ts index 3e0f2fa0..29722c88 100644 --- a/src-web/hooks/useCreateFolder.ts +++ b/src-web/hooks/useCreateFolder.ts @@ -1,5 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; +import { trackEvent } from '../lib/analytics'; import type { Folder } from '../lib/models'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { foldersQueryKey } from './useFolders'; @@ -17,6 +18,7 @@ export function useCreateFolder() { patch.sortPriority = patch.sortPriority || Date.now(); return invoke('create_folder', { workspaceId, ...patch }); }, + onSettled: () => trackEvent('folder', 'create'), onSuccess: async (request) => { await queryClient.invalidateQueries(foldersQueryKey({ workspaceId: request.workspaceId })); }, diff --git a/src-web/hooks/useCreateRequest.ts b/src-web/hooks/useCreateRequest.ts index 9047fa54..926aa89b 100644 --- a/src-web/hooks/useCreateRequest.ts +++ b/src-web/hooks/useCreateRequest.ts @@ -1,5 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; +import { trackEvent } from '../lib/analytics'; import type { HttpRequest } from '../lib/models'; import { useActiveEnvironmentId } from './useActiveEnvironmentId'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; @@ -26,6 +27,7 @@ export function useCreateRequest() { patch.sortPriority = patch.sortPriority || maxSortPriority(requests) + 1000; return invoke('create_request', { workspaceId, ...patch }); }, + onSettled: () => trackEvent('http_request', 'create'), onSuccess: async (request) => { queryClient.setQueryData( requestsQueryKey({ workspaceId: request.workspaceId }), diff --git a/src-web/hooks/useCreateWorkspace.ts b/src-web/hooks/useCreateWorkspace.ts index dfb6ee57..db781b69 100644 --- a/src-web/hooks/useCreateWorkspace.ts +++ b/src-web/hooks/useCreateWorkspace.ts @@ -1,5 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; +import { trackEvent } from '../lib/analytics'; import type { Workspace } from '../lib/models'; import { useAppRoutes } from './useAppRoutes'; import { workspacesQueryKey } from './useWorkspaces'; @@ -11,6 +12,7 @@ export function useCreateWorkspace({ navigateAfter }: { navigateAfter: boolean } mutationFn: (patch) => { return invoke('create_workspace', patch); }, + onSettled: () => trackEvent('workspace', 'create'), onSuccess: async (workspace) => { queryClient.setQueryData(workspacesQueryKey({}), (workspaces) => [ ...(workspaces ?? []), diff --git a/src-web/hooks/useDeleteAnyRequest.tsx b/src-web/hooks/useDeleteAnyRequest.tsx index ac538b40..22d07cab 100644 --- a/src-web/hooks/useDeleteAnyRequest.tsx +++ b/src-web/hooks/useDeleteAnyRequest.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; import { InlineCode } from '../components/core/InlineCode'; +import { trackEvent } from '../lib/analytics'; import type { HttpRequest } from '../lib/models'; import { getRequest } from '../lib/store'; import { useConfirm } from './useConfirm'; @@ -26,6 +27,7 @@ export function useDeleteAnyRequest() { if (!confirmed) return null; return invoke('delete_request', { requestId: id }); }, + onSettled: () => trackEvent('http_request', 'delete'), onSuccess: async (request) => { // Was it cancelled? if (request === null) return; diff --git a/src-web/hooks/useDeleteEnvironment.tsx b/src-web/hooks/useDeleteEnvironment.tsx index 96133b76..53dce26f 100644 --- a/src-web/hooks/useDeleteEnvironment.tsx +++ b/src-web/hooks/useDeleteEnvironment.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; import { InlineCode } from '../components/core/InlineCode'; +import { trackEvent } from '../lib/analytics'; import type { Environment, Workspace } from '../lib/models'; import { useConfirm } from './useConfirm'; import { environmentsQueryKey } from './useEnvironments'; @@ -23,13 +24,13 @@ export function useDeleteEnvironment(environment: Environment | null) { if (!confirmed) return null; return invoke('delete_environment', { environmentId: environment?.id }); }, + onSettled: () => trackEvent('environment', 'delete'), onSuccess: async (environment) => { if (environment === null) return; const { id: environmentId, workspaceId } = environment; - queryClient.setQueryData( - environmentsQueryKey({ workspaceId }), - (environments) => environments?.filter((e) => e.id !== environmentId), + queryClient.setQueryData(environmentsQueryKey({ workspaceId }), (environments) => + environments?.filter((e) => e.id !== environmentId), ); }, }); diff --git a/src-web/hooks/useDeleteFolder.tsx b/src-web/hooks/useDeleteFolder.tsx index 156ec30b..e437fdc1 100644 --- a/src-web/hooks/useDeleteFolder.tsx +++ b/src-web/hooks/useDeleteFolder.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; import { InlineCode } from '../components/core/InlineCode'; +import { trackEvent } from '../lib/analytics'; import type { Folder } from '../lib/models'; import { getFolder } from '../lib/store'; import { useConfirm } from './useConfirm'; @@ -26,6 +27,7 @@ export function useDeleteFolder(id: string | null) { if (!confirmed) return null; return invoke('delete_folder', { folderId: id }); }, + onSettled: () => trackEvent('folder', 'delete'), onSuccess: async (folder) => { // Was it cancelled? if (folder === null) return; diff --git a/src-web/hooks/useDeleteRequest.tsx b/src-web/hooks/useDeleteRequest.tsx index eaab314e..5412d1f2 100644 --- a/src-web/hooks/useDeleteRequest.tsx +++ b/src-web/hooks/useDeleteRequest.tsx @@ -1,40 +1,11 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { invoke } from '@tauri-apps/api'; -import { InlineCode } from '../components/core/InlineCode'; +import { useMutation } from '@tanstack/react-query'; import type { HttpRequest } from '../lib/models'; -import { getRequest } from '../lib/store'; -import { useConfirm } from './useConfirm'; -import { requestsQueryKey } from './useRequests'; -import { responsesQueryKey } from './useResponses'; +import { useDeleteAnyRequest } from './useDeleteAnyRequest'; export function useDeleteRequest(id: string | null) { - const queryClient = useQueryClient(); - const confirm = useConfirm(); + const deleteAnyRequest = useDeleteAnyRequest(); return useMutation({ - mutationFn: async () => { - const request = await getRequest(id); - const confirmed = await confirm({ - title: 'Delete Request', - variant: 'delete', - description: ( - <> - Permanently delete {request?.name}? - - ), - }); - if (!confirmed) return null; - return invoke('delete_request', { requestId: id }); - }, - onSuccess: async (request) => { - // Was it cancelled? - if (request === null) return; - - const { workspaceId, id: requestId } = request; - queryClient.setQueryData(responsesQueryKey({ requestId }), []); // Responses were deleted - queryClient.setQueryData(requestsQueryKey({ workspaceId }), (requests) => - (requests ?? []).filter((r) => r.id !== requestId), - ); - }, + mutationFn: () => deleteAnyRequest.mutateAsync(id ?? 'n/a'), }); } diff --git a/src-web/hooks/useDeleteResponse.ts b/src-web/hooks/useDeleteResponse.ts index 34ea2853..168c68f5 100644 --- a/src-web/hooks/useDeleteResponse.ts +++ b/src-web/hooks/useDeleteResponse.ts @@ -1,5 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; +import { trackEvent } from '../lib/analytics'; import type { HttpResponse } from '../lib/models'; import { responsesQueryKey } from './useResponses'; @@ -9,6 +10,7 @@ export function useDeleteResponse(id: string | null) { mutationFn: async () => { return await invoke('delete_response', { id: id }); }, + onSettled: () => trackEvent('http_response', 'delete'), onSuccess: ({ requestId, id: responseId }) => { queryClient.setQueryData(responsesQueryKey({ requestId }), (responses) => (responses ?? []).filter((response) => response.id !== responseId), diff --git a/src-web/hooks/useDeleteResponses.ts b/src-web/hooks/useDeleteResponses.ts index 39602907..c6dc5e6a 100644 --- a/src-web/hooks/useDeleteResponses.ts +++ b/src-web/hooks/useDeleteResponses.ts @@ -1,5 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; +import { trackEvent } from '../lib/analytics'; import { responsesQueryKey } from './useResponses'; export function useDeleteResponses(requestId?: string) { @@ -9,6 +10,7 @@ export function useDeleteResponses(requestId?: string) { if (requestId === undefined) return; await invoke('delete_all_responses', { requestId }); }, + onSettled: () => trackEvent('http_response', 'delete_many'), onSuccess: async () => { if (requestId === undefined) return; queryClient.setQueryData(responsesQueryKey({ requestId }), []); diff --git a/src-web/hooks/useDeleteWorkspace.tsx b/src-web/hooks/useDeleteWorkspace.tsx index f0024cef..fda3e14b 100644 --- a/src-web/hooks/useDeleteWorkspace.tsx +++ b/src-web/hooks/useDeleteWorkspace.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; import { InlineCode } from '../components/core/InlineCode'; +import { trackEvent } from '../lib/analytics'; import type { Workspace } from '../lib/models'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { useAppRoutes } from './useAppRoutes'; @@ -28,6 +29,7 @@ export function useDeleteWorkspace(workspace: Workspace | null) { if (!confirmed) return null; return invoke('delete_workspace', { workspaceId: workspace?.id }); }, + onSettled: () => trackEvent('workspace', 'delete'), onSuccess: async (workspace) => { if (workspace === null) return; diff --git a/src-web/hooks/useDuplicateRequest.ts b/src-web/hooks/useDuplicateRequest.ts index 45cf0381..a8d013da 100644 --- a/src-web/hooks/useDuplicateRequest.ts +++ b/src-web/hooks/useDuplicateRequest.ts @@ -1,10 +1,11 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; +import { trackEvent } from '../lib/analytics'; import type { HttpRequest } from '../lib/models'; +import { useActiveEnvironmentId } from './useActiveEnvironmentId'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { useAppRoutes } from './useAppRoutes'; import { requestsQueryKey } from './useRequests'; -import { useActiveEnvironmentId } from './useActiveEnvironmentId'; export function useDuplicateRequest({ id, @@ -22,6 +23,7 @@ export function useDuplicateRequest({ if (id === null) throw new Error("Can't duplicate a null request"); return invoke('duplicate_request', { id }); }, + onSettled: () => trackEvent('http_request', 'duplicate'), onSuccess: async (request) => { queryClient.setQueryData( requestsQueryKey({ workspaceId: request.workspaceId }), diff --git a/src-web/hooks/useIntrospectGraphQL.ts b/src-web/hooks/useIntrospectGraphQL.ts index 93fee557..4688d63b 100644 --- a/src-web/hooks/useIntrospectGraphQL.ts +++ b/src-web/hooks/useIntrospectGraphQL.ts @@ -6,8 +6,8 @@ import { minPromiseMillis } from '../lib/minPromiseMillis'; import type { HttpRequest } from '../lib/models'; import { getResponseBodyText } from '../lib/responseBody'; import { sendEphemeralRequest } from '../lib/sendEphemeralRequest'; -import { useDebouncedValue } from './useDebouncedValue'; import { useActiveEnvironmentId } from './useActiveEnvironmentId'; +import { useDebouncedValue } from './useDebouncedValue'; const introspectionRequestBody = JSON.stringify({ query: getIntrospectionQuery(), @@ -66,7 +66,7 @@ export function useIntrospectGraphQL(baseRequest: HttpRequest) { runIntrospection(); // Run immediately // eslint-disable-next-line react-hooks/exhaustive-deps - }, [request.id, request.url, request.method, refetchKey]); + }, [request.id, request.url, request.method, refetchKey, activeEnvironmentId]); const refetch = useCallback(() => { setRefetchKey((k) => k + 1); diff --git a/src-web/hooks/useSendAnyRequest.ts b/src-web/hooks/useSendAnyRequest.ts index 5bf5cf3a..012ab412 100644 --- a/src-web/hooks/useSendAnyRequest.ts +++ b/src-web/hooks/useSendAnyRequest.ts @@ -1,5 +1,6 @@ import { useMutation } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; +import { trackEvent } from '../lib/analytics'; import type { HttpResponse } from '../lib/models'; import { useActiveEnvironmentId } from './useActiveEnvironmentId'; @@ -7,5 +8,6 @@ export function useSendAnyRequest() { const environmentId = useActiveEnvironmentId(); return useMutation({ mutationFn: (id) => invoke('send_request', { requestId: id, environmentId }), + onSettled: () => trackEvent('http_request', 'send'), }); } diff --git a/src-web/hooks/useUpdateAnyRequest.ts b/src-web/hooks/useUpdateAnyRequest.ts index ab9f80f9..7a4f447f 100644 --- a/src-web/hooks/useUpdateAnyRequest.ts +++ b/src-web/hooks/useUpdateAnyRequest.ts @@ -7,20 +7,28 @@ import { requestsQueryKey } from './useRequests'; export function useUpdateAnyRequest() { const queryClient = useQueryClient(); - return useMutation HttpRequest }>({ + return useMutation< + void, + unknown, + { id: string; update: Partial | ((r: HttpRequest) => HttpRequest) } + >({ mutationFn: async ({ id, update }) => { const request = await getRequest(id); if (request === null) { throw new Error("Can't update a null request"); } - await invoke('update_request', { request: update(request) }); + const patchedRequest = + typeof update === 'function' ? update(request) : { ...request, ...update }; + await invoke('update_request', { request: patchedRequest }); }, onMutate: async ({ id, update }) => { const request = await getRequest(id); if (request === null) return; + const patchedRequest = + typeof update === 'function' ? update(request) : { ...request, ...update }; queryClient.setQueryData(requestsQueryKey(request), (requests) => - (requests ?? []).map((r) => (r.id === request.id ? update(r) : r)), + (requests ?? []).map((r) => (r.id === patchedRequest.id ? patchedRequest : r)), ); }, }); diff --git a/src-web/hooks/useUpdateRequest.ts b/src-web/hooks/useUpdateRequest.ts index 808e9859..6c6b82d6 100644 --- a/src-web/hooks/useUpdateRequest.ts +++ b/src-web/hooks/useUpdateRequest.ts @@ -1,29 +1,10 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { invoke } from '@tauri-apps/api'; +import { useMutation } from '@tanstack/react-query'; import type { HttpRequest } from '../lib/models'; -import { getRequest } from '../lib/store'; -import { requestsQueryKey } from './useRequests'; +import { useUpdateAnyRequest } from './useUpdateAnyRequest'; export function useUpdateRequest(id: string | null) { - const queryClient = useQueryClient(); + const updateAnyRequest = useUpdateAnyRequest(); return useMutation | ((r: HttpRequest) => HttpRequest)>({ - mutationFn: async (v) => { - const request = await getRequest(id); - if (request == null) { - throw new Error("Can't update a null request"); - } - - const newRequest = typeof v === 'function' ? v(request) : { ...request, ...v }; - await invoke('update_request', { request: newRequest }); - }, - onMutate: async (v) => { - const request = await getRequest(id); - if (request === null) return; - - const patchedRequest = typeof v === 'function' ? v(request) : { ...request, ...v }; - queryClient.setQueryData(requestsQueryKey(request), (requests) => - (requests ?? []).map((r) => (r.id === patchedRequest.id ? patchedRequest : r)), - ); - }, + mutationFn: async (update) => updateAnyRequest.mutateAsync({ id: id ?? 'n/a', update }), }); } diff --git a/src-web/lib/analytics.ts b/src-web/lib/analytics.ts new file mode 100644 index 00000000..2293bff3 --- /dev/null +++ b/src-web/lib/analytics.ts @@ -0,0 +1,58 @@ +import { getVersion } from '@tauri-apps/api/app'; +import type { Environment, Folder, HttpRequest, HttpResponse, KeyValue, Workspace } from './models'; + +const appVersion = await getVersion(); + +export function trackEvent( + resource: + | Workspace['model'] + | Environment['model'] + | Folder['model'] + | HttpRequest['model'] + | HttpResponse['model'] + | KeyValue['model'], + event: 'create' | 'update' | 'delete' | 'delete_many' | 'send' | 'duplicate', + attributes: Record = {}, +) { + send('/e', [ + { name: 'e', value: `${resource}.${event}` }, + { name: 'a', value: JSON.stringify({ ...attributes, version: appVersion }) }, + ]); +} + +export function trackPage(pathname: string) { + if (pathname === sessionStorage.lastPathName) { + return; + } + + sessionStorage.lastPathName = pathname; + send('/p', [ + { + name: 'h', + value: 'desktop.yaak.app', + }, + { name: 'p', value: pathname }, + ]); +} + +function send(path: string, params: { name: string; value: string | number }[]) { + if (localStorage.disableAnalytics === 'true') { + console.log('Analytics disabled', path, params); + } + + params.push({ name: 'id', value: 'site_zOK0d7jeBy2TLxFCnZ' }); + params.push({ + name: 'tz', + value: Intl.DateTimeFormat().resolvedOptions().timeZone, + }); + params.push({ name: 'xy', value: screensize() }); + const qs = params.map((v) => `${v.name}=${encodeURIComponent(v.value)}`).join('&'); + const url = `https://t.yaak.app/t${path}?${qs}`; + fetch(url, { mode: 'no-cors' }).catch((err) => console.log('Error:', err)); +} + +function screensize() { + const w = window.screen.width; + const h = window.screen.height; + return `${Math.round(w / 100) * 100}x${Math.round(h / 100) * 100}`; +}