Skip to content

Commit 1cfd22c

Browse files
committed
feat: add tracker
1 parent 4adf672 commit 1cfd22c

File tree

11 files changed

+181
-39
lines changed

11 files changed

+181
-39
lines changed

src/app/analyze.tsx

+34-21
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22

33
import { useRef } from 'react'
44
import { useServerInsertedHTML } from 'next/navigation'
5+
import type { TrackerAction } from '~/constants/tracker'
56

6-
import { apiClient } from '~/lib/request'
7+
declare global {
8+
interface Window {
9+
umami?: {
10+
track: (event: string, data?: any) => void
11+
}
12+
}
13+
}
714

815
export const Analyze = () => {
916
const onceRef = useRef(false)
@@ -13,33 +20,39 @@ export const Analyze = () => {
1320
}
1421

1522
onceRef.current = true
16-
const apiBase = apiClient.proxy.fn.utils.analyze.toString(true)
23+
// const apiBase = apiClient.proxy.fn.utils.analyze.toString(true)
1724

1825
return (
1926
<script
2027
dangerouslySetInnerHTML={{
2128
__html:
22-
`var apiBase = "${apiBase}";
23-
${function run() {
24-
document.addEventListener('click', async (e) => {
25-
const $ = e.target as HTMLElement
26-
const event = $.dataset.event
27-
28-
if (event) {
29-
await fetch(apiBase, {
30-
method: 'POST',
31-
headers: {
32-
'Content-Type': 'application/json',
29+
`${function run() {
30+
document.addEventListener(
31+
'click',
32+
async (e) => {
33+
const $ = e.target as HTMLElement
34+
const event = $.dataset.event
35+
36+
if (event) {
37+
window.umami?.track(event, {
38+
type: 'click',
39+
})
40+
}
3341
},
34-
body: JSON.stringify({
35-
key: 'mx',
36-
event,
37-
type: 'inc',
38-
}),
42+
true,
43+
)
44+
45+
document.addEventListener('impression', async (e: any) => {
46+
const detail = e.detail as {
47+
action: TrackerAction
48+
label: string
49+
}
50+
51+
window.umami?.track(detail.label, {
52+
type: 'impression',
53+
})
3954
})
40-
}
41-
})
42-
}.toString()}\n` + `run();`,
55+
}.toString()}\n` + `run();`,
4356
}}
4457
/>
4558
)

src/app/config.d.ts

+11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
1+
import type { ScriptProps } from 'next/script'
2+
13
export interface AppConfig {
24
site: Site
35
hero: Hero
46
module: Module
7+
8+
custom: Custom
59
}
10+
export interface Custom {
11+
css: string[]
12+
styles: any[]
13+
js: string[]
14+
scripts: ScriptProps[]
15+
}
16+
617
export interface Site {
718
favicon: string
819
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { memo, useState } from 'react'
2+
import { useInView } from 'react-intersection-observer'
3+
4+
import { useIsLogged } from '~/atoms'
5+
import { TrackerAction } from '~/constants/tracker'
6+
7+
type ImpressionProps = {
8+
trackerMessage?: string
9+
action?: TrackerAction
10+
onTrack?: () => any
11+
}
12+
export const ImpressionView: Component<
13+
{ shouldTrack?: boolean } & ImpressionProps
14+
> = (props) => {
15+
const { shouldTrack = true, ...rest } = props
16+
if (!shouldTrack) {
17+
return <>{props.children}</>
18+
}
19+
return <ImpressionViewImpl {...rest} />
20+
}
21+
22+
const ImpressionViewImpl: Component<ImpressionProps> = memo((props) => {
23+
const [impression, setImpression] = useState(false)
24+
const isLogged = useIsLogged()
25+
const { ref } = useInView({
26+
initialInView: false,
27+
triggerOnce: true,
28+
onChange(inView) {
29+
if (inView) {
30+
setImpression(true)
31+
32+
if (isLogged) {
33+
return
34+
}
35+
document.dispatchEvent(
36+
new CustomEvent('impression', {
37+
detail: {
38+
action: props.action ?? TrackerAction.Impression,
39+
label: props.trackerMessage,
40+
},
41+
}),
42+
)
43+
44+
props.onTrack?.()
45+
}
46+
},
47+
})
48+
49+
return (
50+
<>
51+
{props.children}
52+
{!impression && <span ref={ref} />}
53+
</>
54+
)
55+
})
56+
57+
ImpressionViewImpl.displayName = 'ImpressionView'

src/components/layout/header/internal/Activity.tsx

+11-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import {
1111
setActivityProcessName,
1212
useActivity,
1313
} from '~/atoms/activity'
14+
import { ImpressionView } from '~/components/common/ImpressionTracker'
1415
import { FloatPopover } from '~/components/ui/float-popover'
1516
import { softBouncePrest } from '~/constants/spring'
17+
import { TrackerAction } from '~/constants/tracker'
1618
import useDebounceValue from '~/hooks/common/use-debounce-value'
1719
import { usePageIsActive } from '~/hooks/common/use-is-active'
1820
import { apiClient } from '~/lib/request'
@@ -163,10 +165,15 @@ export const Activity = memo(() => {
163165
type="tooltip"
164166
strategy="fixed"
165167
>
166-
{ownerName} 正在使用 {processName}
167-
{appDescrption[processName]
168-
? ` ${appDescrption[processName]}`
169-
: ''}
168+
<ImpressionView
169+
action={TrackerAction.Impression}
170+
trackerMessage="Activity"
171+
>
172+
{ownerName} 正在使用 {processName}
173+
{appDescrption[processName]
174+
? ` ${appDescrption[processName]}`
175+
: ''}
176+
</ImpressionView>
170177
</FloatPopover>
171178
</m.div>
172179
)}

src/components/widgets/peek/PeekLink.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const PeekLink: FC<
2727
)
2828

2929
return (
30-
<Link href={href} onClick={handlePeek} data-event="peek" {...rest}>
30+
<Link href={href} onClick={handlePeek} {...rest}>
3131
{children}
3232
</Link>
3333
)

src/components/widgets/peek/PeekModal.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { m } from 'framer-motion'
22
import Link from 'next/link'
33
import type { PropsWithChildren } from 'react'
44

5+
import { ImpressionView } from '~/components/common/ImpressionTracker'
56
import { microReboundPreset } from '~/constants/spring'
7+
import { TrackerAction } from '~/constants/tracker'
68
import { useModalStack } from '~/providers/root/modal-stack-provider'
79

810
export const PeekModal = (
@@ -14,6 +16,10 @@ export const PeekModal = (
1416

1517
return (
1618
<div>
19+
<ImpressionView
20+
action={TrackerAction.Impression}
21+
trackerMessage="Peek Modal"
22+
/>
1723
<m.div
1824
initial={{ opacity: 0.5, y: 50 }}
1925
animate={{ opacity: 1, y: 0 }}

src/components/widgets/shared/AsideDonateButton.tsx

+20-13
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { AnimatePresence, m } from 'framer-motion'
33
import { atom, useAtomValue, useSetAtom } from 'jotai'
44
import type { HTMLMotionProps } from 'framer-motion'
55

6+
import { ImpressionView } from '~/components/common/ImpressionTracker'
67
import { MotionButtonBase } from '~/components/ui/button'
78
import { DialogOverlay } from '~/components/ui/dlalog/DialogOverlay'
9+
import { TrackerAction } from '~/constants/tracker'
810
import { useIsClient } from '~/hooks/common/use-is-client'
911
import { clsxm } from '~/lib/helper'
1012
import { useAppConfigSelector } from '~/providers/root/aggregation-data-provider'
@@ -80,19 +82,24 @@ const DonateButtonTop = () => {
8082
const setOverlayShow = useSetAtom(overlayShowAtom)
8183
const buttonPos = useAtomValue(positionAtom)
8284
return (
83-
<DonateButtonInternal
84-
className="focus-visible:text-uk-brown-light focus-visible:!shadow-none"
85-
style={{
86-
position: 'fixed',
87-
left: buttonPos.x,
88-
top: buttonPos.y,
89-
zIndex: 999,
90-
margin: 0,
91-
}}
92-
onMouseLeave={() => {
93-
setOverlayShow(false)
94-
}}
95-
/>
85+
<ImpressionView
86+
trackerMessage="Donate Show"
87+
action={TrackerAction.Impression}
88+
>
89+
<DonateButtonInternal
90+
className="focus-visible:text-uk-brown-light focus-visible:!shadow-none"
91+
style={{
92+
position: 'fixed',
93+
left: buttonPos.x,
94+
top: buttonPos.y,
95+
zIndex: 999,
96+
margin: 0,
97+
}}
98+
onMouseLeave={() => {
99+
setOverlayShow(false)
100+
}}
101+
/>
102+
</ImpressionView>
96103
)
97104
}
98105

src/components/widgets/shared/AutoResizeHeight.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const AutoResizeHeight: React.FC<AnimateChangeInHeightProps> = ({
3838
<m.div
3939
className={clsxm('overflow-hidden', className)}
4040
style={{ height }}
41+
initial={false}
4142
animate={{ height }}
4243
transition={{ duration }}
4344
>

src/providers/root/index.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { EventProvider } from './event-provider'
1515
import { JotaiStoreProvider } from './jotai-provider'
1616
import { ModalStackProvider } from './modal-stack-provider'
1717
import { PageScrollInfoProvider } from './page-scroll-info-provider'
18+
import { ScriptInjectProvider } from './script-inject-provider'
1819
import { SentryProvider } from './sentry-provider'
1920
import { SocketContainer } from './socket-provider'
2021

@@ -41,6 +42,7 @@ export function Providers({ children }: PropsWithChildren) {
4142
<PageScrollInfoProvider key="PageScrollInfoProvider" />
4243
<DebugProvider key="debugProvider" />
4344
<AccentColorProvider />
45+
<ScriptInjectProvider />
4446
</ProviderComposer>
4547
</>
4648
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use client'
2+
3+
import Script from 'next/script'
4+
5+
import { useAppConfigSelector } from './aggregation-data-provider'
6+
7+
export const ScriptInjectProvider = () => {
8+
const scripts = useAppConfigSelector((config) => config.custom.scripts)
9+
if (!scripts) return null
10+
return (
11+
<>
12+
{scripts.map((props) => (
13+
<Script key={props.src} {...props} />
14+
))}
15+
</>
16+
)
17+
}

0 commit comments

Comments
 (0)