Skip to content

Commit b0b22ee

Browse files
Nick changes
1 parent d01a578 commit b0b22ee

File tree

5 files changed

+227
-32
lines changed

5 files changed

+227
-32
lines changed

src/services/events/amplitude/amplitude.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import * as amplitude from '@amplitude/analytics-browser'
22

3+
import config from 'config'
4+
35
import { providerToInternalProvider } from 'shared/utils/provider'
46

57
import { Event, EventContext, EventTracker, Identity } from '../types'
68

7-
const AMPLITUDE_API_KEY = process.env.REACT_APP_AMPLITUDE_API_KEY
9+
const AMPLITUDE_API_KEY = config.AMPLITUDE_API_KEY
810

911
export function initAmplitude() {
1012
if (!AMPLITUDE_API_KEY) {
@@ -24,7 +26,7 @@ export class AmplitudeEventTracker implements EventTracker {
2426
identity?: Identity
2527

2628
identify(identity: Identity) {
27-
if (JSON.stringify(this.identity) === JSON.stringify(identity)) {
29+
if (this.identity?.userOwnerId === identity.userOwnerId) {
2830
// Don't identify this user again this session.
2931
return
3032
}

src/services/events/events.ts

+34-21
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import { captureException } from '@sentry/react'
2-
import { useEffect } from 'react'
2+
import { useQuery as useQueryV5 } from '@tanstack/react-queryV5'
3+
import { useRef } from 'react'
34
import { useParams } from 'react-router'
45

5-
import { useRepo } from 'services/repo'
6-
import { useOwner } from 'services/user'
6+
import { Provider } from 'shared/api/helpers'
77

88
import { AmplitudeEventTracker, initAmplitude } from './amplitude/amplitude'
9-
import { StubbedEventTracker } from './stub'
10-
import { EventTracker } from './types'
9+
import { OwnerContextQueryOpts, RepoContextQueryOpts } from './hooks'
10+
import { Event, EventContext, EventTracker, Identity } from './types'
11+
12+
class StubbedEventTracker implements EventTracker {
13+
identify(_identity: Identity): void {}
14+
track(_event: Event): void {}
15+
setContext(_context: EventContext): void {}
16+
}
1117

1218
// EventTracker singleton
1319
let EVENT_TRACKER: EventTracker = new StubbedEventTracker()
@@ -19,7 +25,7 @@ export function initEventTracker(): void {
1925
EVENT_TRACKER = new AmplitudeEventTracker()
2026
} catch (e) {
2127
if (process.env.REACT_APP_ENV === 'production') {
22-
// If in production, we need to know this has occured.
28+
// If in production, we need to know this has occurred.
2329
captureException(e)
2430
}
2531
}
@@ -33,32 +39,39 @@ export function eventTracker(): EventTracker {
3339
// Hook to keep the global EventTracker's context up-to-date.
3440
export function useEventContext() {
3541
const { provider, owner, repo } = useParams<{
36-
provider?: string
42+
provider: Provider
3743
owner?: string
3844
repo?: string
3945
}>()
46+
const context = useRef<EventContext>({})
4047

41-
const { data: ownerData } = useOwner({ username: owner })
42-
const { data: repoData } = useRepo({
43-
provider: provider || '',
44-
owner: owner || '',
45-
repo: repo || '',
46-
opts: { enabled: !!(provider && owner && repo) },
47-
})
48+
const { data: ownerData } = useQueryV5(
49+
OwnerContextQueryOpts({ provider, owner })
50+
)
51+
const { data: repoData } = useQueryV5(
52+
RepoContextQueryOpts({ provider, owner, repo })
53+
)
4854

49-
useEffect(() => {
50-
EVENT_TRACKER.setContext({
55+
if (
56+
ownerData?.ownerid !== context.current.owner?.id ||
57+
repoData?.repoid !== context.current.repo?.id
58+
) {
59+
// only update if this is a new owner or repo
60+
const newContext: EventContext = {
5161
owner: ownerData?.ownerid
5262
? {
5363
id: ownerData?.ownerid,
5464
}
5565
: undefined,
56-
repo: repoData?.repository
66+
repo: repoData?.repoid
5767
? {
58-
id: repoData?.repository?.repoid,
59-
isPrivate: repoData?.repository.private || undefined,
68+
id: repoData.repoid,
69+
isPrivate: repoData.private === null ? undefined : repoData.private,
6070
}
6171
: undefined,
62-
})
63-
}, [ownerData, repoData])
72+
}
73+
console.log(newContext)
74+
EVENT_TRACKER.setContext(newContext)
75+
context.current = newContext
76+
}
6477
}

src/services/events/hooks.tsx

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import { queryOptions as queryOptionsV5 } from '@tanstack/react-queryV5'
2+
import { z } from 'zod'
3+
4+
import {
5+
RepoNotFoundErrorSchema,
6+
RepoOwnerNotActivatedErrorSchema,
7+
} from 'services/repo'
8+
import Api from 'shared/api'
9+
import { Provider, rejectNetworkError } from 'shared/api/helpers'
10+
import A from 'ui/A'
11+
12+
const OwnerContextSchema = z.object({
13+
owner: z
14+
.object({
15+
ownerid: z.number().nullable(),
16+
})
17+
.nullable(),
18+
})
19+
20+
const ownerContextQuery = `
21+
query OwnerContext($owner: String!) {
22+
owner(username: $owner) {
23+
ownerid
24+
}
25+
}
26+
`
27+
28+
interface OwnerContextQueryOptsArgs {
29+
provider: Provider
30+
owner?: string
31+
}
32+
33+
export const OwnerContextQueryOpts = ({
34+
provider,
35+
owner,
36+
}: OwnerContextQueryOptsArgs) =>
37+
queryOptionsV5({
38+
queryKey: ['OwnerContext', provider, owner],
39+
queryFn: ({ signal }) =>
40+
Api.graphql({
41+
provider,
42+
query: ownerContextQuery,
43+
signal,
44+
variables: {
45+
owner,
46+
},
47+
}).then((res) => {
48+
const parsedRes = OwnerContextSchema.safeParse(res.data)
49+
50+
console.log('aewsr')
51+
52+
if (!parsedRes.success) {
53+
return rejectNetworkError({
54+
status: 404,
55+
data: {},
56+
dev: 'OwnerContextQueryOpts - 404 Failed to parse data',
57+
})
58+
}
59+
60+
return parsedRes.data.owner
61+
}),
62+
enabled: !!owner,
63+
// Fetch this data only once per session
64+
staleTime: Infinity,
65+
gcTime: Infinity,
66+
})
67+
68+
const RepositorySchema = z.object({
69+
__typename: z.literal('Repository'),
70+
repoid: z.number(),
71+
private: z.boolean().nullable(),
72+
})
73+
74+
const RepoContextSchema = z.object({
75+
owner: z
76+
.object({
77+
repository: z
78+
.discriminatedUnion('__typename', [
79+
RepositorySchema,
80+
RepoNotFoundErrorSchema,
81+
RepoOwnerNotActivatedErrorSchema,
82+
])
83+
.nullable(),
84+
})
85+
.nullable(),
86+
})
87+
88+
const repoContextQuery = `
89+
query RepoContext($owner: String!, $repo: String!) {
90+
owner(username: $owner) {
91+
repository(name: $repo) {
92+
__typename
93+
... on Repository {
94+
repoid
95+
private
96+
}
97+
... on NotFoundError {
98+
message
99+
}
100+
... on OwnerNotActivatedError {
101+
message
102+
}
103+
}
104+
}
105+
}
106+
`
107+
108+
interface RepoContextQueryOptsArgs {
109+
provider: Provider
110+
owner?: string
111+
repo?: string
112+
}
113+
114+
export const RepoContextQueryOpts = ({
115+
provider,
116+
owner,
117+
repo,
118+
}: RepoContextQueryOptsArgs) =>
119+
queryOptionsV5({
120+
queryKey: ['RepoContext', provider, owner, repo],
121+
queryFn: ({ signal }) =>
122+
Api.graphql({
123+
provider,
124+
query: repoContextQuery,
125+
signal,
126+
variables: {
127+
owner,
128+
repo,
129+
},
130+
}).then((res) => {
131+
const parsedRes = RepoContextSchema.safeParse(res.data)
132+
133+
if (!parsedRes.success) {
134+
return rejectNetworkError({
135+
status: 404,
136+
data: {},
137+
dev: 'RepoContextQueryOpts - 404 Failed to parse data',
138+
})
139+
}
140+
141+
if (parsedRes.data?.owner?.repository?.__typename === 'NotFoundError') {
142+
return rejectNetworkError({
143+
status: 404,
144+
data: {},
145+
dev: 'RepoContextQueryOpts - 404 NotFoundError',
146+
})
147+
}
148+
149+
if (
150+
parsedRes.data?.owner?.repository?.__typename ===
151+
'OwnerNotActivatedError'
152+
) {
153+
return rejectNetworkError({
154+
status: 403,
155+
data: {
156+
detail: (
157+
<p>
158+
Activation is required to view this repo, please{' '}
159+
{/* @ts-expect-error - A hasn't been typed yet */}
160+
<A to={{ pageName: 'membersTab' }}>click here </A> to activate
161+
your account.
162+
</p>
163+
),
164+
},
165+
dev: 'RepoContextQueryOpts - 404 OwnerNotActivatedError',
166+
})
167+
}
168+
169+
return parsedRes.data.owner?.repository
170+
}),
171+
enabled: !!owner && !!repo,
172+
// Fetch this data only once per session
173+
staleTime: Infinity,
174+
gcTime: Infinity,
175+
})

src/services/events/stub.ts

-7
This file was deleted.

src/services/events/types.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,23 @@ import { Provider } from 'shared/api/helpers'
1818
// accidental correlation of unrelated events.
1919
// - Never include names, only use ids. E.g., use repoid instead of repo name.
2020
//
21+
//
22+
// If this union type grows too unwieldy, we can split each event out into its
23+
// own type.
24+
//
2125

2226
export type Event =
2327
| {
2428
type: 'Button Clicked'
2529
properties: {
26-
buttonType: 'Install GitHub App' | 'Configure Repo'
30+
buttonType: ButtonType
2731
buttonLocation?: string
2832
}
2933
}
3034
| {
3135
type: 'Page Viewed'
3236
properties: {
33-
pageName: 'Owner Page'
37+
pageName: PageName
3438
}
3539
}
3640

@@ -74,3 +78,11 @@ export abstract class EventTracker {
7478
)
7579
}
7680
}
81+
82+
//
83+
// String union types to make the above Event type easier to visually parse.
84+
// Extend as needed.
85+
//
86+
87+
type ButtonType = 'Install GitHub App' | 'Configure Repo'
88+
type PageName = 'Owner Page'

0 commit comments

Comments
 (0)