Tracking (#511)
* tracking helper functions * track everything * remove extraneous code
This commit is contained in:
parent
730b7272ce
commit
c45da8c334
|
@ -61,7 +61,7 @@ export function AddFundsButton(props: { className?: string }) {
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-primary bg-gradient-to-r from-teal-500 to-green-500 px-10 font-medium hover:from-teal-600 hover:to-green-600"
|
className="btn btn-primary bg-gradient-to-r from-indigo-500 to-blue-500 px-10 font-medium hover:from-indigo-600 hover:to-blue-600"
|
||||||
>
|
>
|
||||||
Checkout
|
Checkout
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -22,9 +22,9 @@ import {
|
||||||
calculateDpmPayoutAfterCorrectBet,
|
calculateDpmPayoutAfterCorrectBet,
|
||||||
getDpmOutcomeProbabilityAfterBet,
|
getDpmOutcomeProbabilityAfterBet,
|
||||||
} from 'common/calculate-dpm'
|
} from 'common/calculate-dpm'
|
||||||
import { firebaseLogin } from 'web/lib/firebase/users'
|
|
||||||
import { Bet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
import { SignUpPrompt } from '../sign-up-prompt'
|
||||||
|
|
||||||
export function AnswerBetPanel(props: {
|
export function AnswerBetPanel(props: {
|
||||||
answer: Answer
|
answer: Answer
|
||||||
|
@ -183,12 +183,7 @@ export function AnswerBetPanel(props: {
|
||||||
{isSubmitting ? 'Submitting...' : 'Submit trade'}
|
{isSubmitting ? 'Submitting...' : 'Submit trade'}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<SignUpPrompt />
|
||||||
className="btn self-stretch whitespace-nowrap border-none bg-gradient-to-r from-teal-500 to-green-500 px-10 text-lg font-medium normal-case hover:from-teal-600 hover:to-green-600"
|
|
||||||
onClick={firebaseLogin}
|
|
||||||
>
|
|
||||||
Sign up to bet!
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
import { firebaseLogin } from 'web/lib/firebase/users'
|
import { firebaseLogin } from 'web/lib/firebase/users'
|
||||||
import { Bet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
import { MAX_ANSWER_LENGTH } from 'common/answer'
|
import { MAX_ANSWER_LENGTH } from 'common/answer'
|
||||||
|
import { withTracking } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
|
export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
|
||||||
const { contract } = props
|
const { contract } = props
|
||||||
|
@ -143,7 +144,7 @@ export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
|
||||||
isSubmitting && 'loading'
|
isSubmitting && 'loading'
|
||||||
)}
|
)}
|
||||||
disabled={!canSubmit}
|
disabled={!canSubmit}
|
||||||
onClick={submitAnswer}
|
onClick={withTracking(submitAnswer, 'submit answer')}
|
||||||
>
|
>
|
||||||
Submit answer & buy
|
Submit answer & buy
|
||||||
</button>
|
</button>
|
||||||
|
@ -151,7 +152,7 @@ export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
|
||||||
text && (
|
text && (
|
||||||
<button
|
<button
|
||||||
className="btn self-end whitespace-nowrap border-none bg-gradient-to-r from-teal-500 to-green-500 px-10 text-lg font-medium normal-case hover:from-teal-600 hover:to-green-600"
|
className="btn self-end whitespace-nowrap border-none bg-gradient-to-r from-teal-500 to-green-500 px-10 text-lg font-medium normal-case hover:from-teal-600 hover:to-green-600"
|
||||||
onClick={firebaseLogin}
|
onClick={withTracking(firebaseLogin, 'answer panel sign in')}
|
||||||
>
|
>
|
||||||
Sign in
|
Sign in
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -26,6 +26,8 @@ import { EditCategoriesButton } from './feed/category-selector'
|
||||||
import { CATEGORIES } from 'common/categories'
|
import { CATEGORIES } from 'common/categories'
|
||||||
import { Tabs } from './layout/tabs'
|
import { Tabs } from './layout/tabs'
|
||||||
import { EditFollowingButton } from './following-button'
|
import { EditFollowingButton } from './following-button'
|
||||||
|
import { track } from '@amplitude/analytics-browser'
|
||||||
|
import { trackCallback } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
const searchClient = algoliasearch(
|
const searchClient = algoliasearch(
|
||||||
'GJQPAYENIF',
|
'GJQPAYENIF',
|
||||||
|
@ -129,11 +131,13 @@ export function ContractSearch(props: {
|
||||||
input: '!pl-10 !input !input-bordered shadow-none w-[100px]',
|
input: '!pl-10 !input !input-bordered shadow-none w-[100px]',
|
||||||
resetIcon: 'mt-2 hidden sm:flex',
|
resetIcon: 'mt-2 hidden sm:flex',
|
||||||
}}
|
}}
|
||||||
|
onBlur={trackCallback('search')}
|
||||||
/>
|
/>
|
||||||
<select
|
<select
|
||||||
className="!select !select-bordered"
|
className="!select !select-bordered"
|
||||||
value={filter}
|
value={filter}
|
||||||
onChange={(e) => setFilter(e.target.value as filter)}
|
onChange={(e) => setFilter(e.target.value as filter)}
|
||||||
|
onBlur={trackCallback('select search filter')}
|
||||||
>
|
>
|
||||||
<option value="open">Open</option>
|
<option value="open">Open</option>
|
||||||
<option value="closed">Closed</option>
|
<option value="closed">Closed</option>
|
||||||
|
@ -145,6 +149,7 @@ export function ContractSearch(props: {
|
||||||
classNames={{
|
classNames={{
|
||||||
select: '!select !select-bordered',
|
select: '!select !select-bordered',
|
||||||
}}
|
}}
|
||||||
|
onBlur={trackCallback('select search sort')}
|
||||||
/>
|
/>
|
||||||
<Configure
|
<Configure
|
||||||
facetFilters={filters}
|
facetFilters={filters}
|
||||||
|
@ -296,7 +301,11 @@ function CategoryFollowSelector(props: {
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
]}
|
]}
|
||||||
onClick={(_, index) => setMode(index === 0 ? 'categories' : 'following')}
|
onClick={(_, index) => {
|
||||||
|
const mode = index === 0 ? 'categories' : 'following'
|
||||||
|
setMode(mode)
|
||||||
|
track(`click ${mode} tab`)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import { getExpectedValue, getValueFromBucket } from 'common/calculate-dpm'
|
||||||
import { QuickBet, ProbBar, getColor } from './quick-bet'
|
import { QuickBet, ProbBar, getColor } from './quick-bet'
|
||||||
import { useContractWithPreload } from 'web/hooks/use-contract'
|
import { useContractWithPreload } from 'web/hooks/use-contract'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
|
import { track } from '@amplitude/analytics-browser'
|
||||||
|
import { trackCallback } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function ContractCard(props: {
|
export function ContractCard(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
@ -71,12 +73,22 @@ export function ContractCard(props: {
|
||||||
if (e.ctrlKey || e.metaKey) return
|
if (e.ctrlKey || e.metaKey) return
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
track('click market card', {
|
||||||
|
slug: contract.slug,
|
||||||
|
contractId: contract.id,
|
||||||
|
})
|
||||||
onClick()
|
onClick()
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Link href={contractPath(contract)}>
|
<Link href={contractPath(contract)}>
|
||||||
<a className="absolute top-0 left-0 right-0 bottom-0" />
|
<a
|
||||||
|
onClick={trackCallback('click market card', {
|
||||||
|
slug: contract.slug,
|
||||||
|
contractId: contract.id,
|
||||||
|
})}
|
||||||
|
className="absolute top-0 left-0 right-0 bottom-0"
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,11 +2,13 @@ import React, { Fragment } from 'react'
|
||||||
import { LinkIcon } from '@heroicons/react/outline'
|
import { LinkIcon } from '@heroicons/react/outline'
|
||||||
import { Menu, Transition } from '@headlessui/react'
|
import { Menu, Transition } from '@headlessui/react'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|
||||||
import { Contract } from 'common/contract'
|
import { Contract } from 'common/contract'
|
||||||
import { copyToClipboard } from 'web/lib/util/copy'
|
import { copyToClipboard } from 'web/lib/util/copy'
|
||||||
import { contractPath } from 'web/lib/firebase/contracts'
|
import { contractPath } from 'web/lib/firebase/contracts'
|
||||||
import { ENV_CONFIG } from 'common/envs/constants'
|
import { ENV_CONFIG } from 'common/envs/constants'
|
||||||
import { ToastClipboard } from 'web/components/toast-clipboard'
|
import { ToastClipboard } from 'web/components/toast-clipboard'
|
||||||
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
function copyContractUrl(contract: Contract) {
|
function copyContractUrl(contract: Contract) {
|
||||||
copyToClipboard(`https://${ENV_CONFIG.domain}${contractPath(contract)}`)
|
copyToClipboard(`https://${ENV_CONFIG.domain}${contractPath(contract)}`)
|
||||||
|
@ -23,7 +25,10 @@ export function CopyLinkButton(props: {
|
||||||
<Menu
|
<Menu
|
||||||
as="div"
|
as="div"
|
||||||
className="relative z-10 flex-shrink-0"
|
className="relative z-10 flex-shrink-0"
|
||||||
onMouseUp={() => copyContractUrl(contract)}
|
onMouseUp={() => {
|
||||||
|
copyContractUrl(contract)
|
||||||
|
track('copy share link')
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Menu.Button
|
<Menu.Button
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { Col } from '../layout/col'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { updateUser, User } from 'web/lib/firebase/users'
|
import { updateUser, User } from 'web/lib/firebase/users'
|
||||||
import { Checkbox } from '../checkbox'
|
import { Checkbox } from '../checkbox'
|
||||||
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function CategorySelector(props: {
|
export function CategorySelector(props: {
|
||||||
category: string
|
category: string
|
||||||
|
@ -93,7 +94,10 @@ export function EditCategoriesButton(props: {
|
||||||
className,
|
className,
|
||||||
'btn btn-sm btn-ghost cursor-pointer gap-2 whitespace-nowrap text-sm normal-case text-gray-700'
|
'btn btn-sm btn-ghost cursor-pointer gap-2 whitespace-nowrap text-sm normal-case text-gray-700'
|
||||||
)}
|
)}
|
||||||
onClick={() => setIsOpen(true)}
|
onClick={() => {
|
||||||
|
setIsOpen(true)
|
||||||
|
track('edit categories button')
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<PencilIcon className="inline h-4 w-4" />
|
<PencilIcon className="inline h-4 w-4" />
|
||||||
Categories
|
Categories
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { Col } from 'web/components/layout/col'
|
||||||
import { getProbability } from 'common/calculate'
|
import { getProbability } from 'common/calculate'
|
||||||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||||
import { PaperAirplaneIcon } from '@heroicons/react/outline'
|
import { PaperAirplaneIcon } from '@heroicons/react/outline'
|
||||||
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function FeedCommentThread(props: {
|
export function FeedCommentThread(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
@ -354,6 +355,7 @@ export function CommentInput(props: {
|
||||||
|
|
||||||
async function submitComment(betId: string | undefined) {
|
async function submitComment(betId: string | undefined) {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
track('sign in to comment')
|
||||||
return await firebaseLogin()
|
return await firebaseLogin()
|
||||||
}
|
}
|
||||||
if (!comment || isSubmitting) return
|
if (!comment || isSubmitting) return
|
||||||
|
|
|
@ -2,6 +2,7 @@ import clsx from 'clsx'
|
||||||
import { useFollows } from 'web/hooks/use-follows'
|
import { useFollows } from 'web/hooks/use-follows'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { follow, unfollow } from 'web/lib/firebase/users'
|
import { follow, unfollow } from 'web/lib/firebase/users'
|
||||||
|
import { withTracking } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function FollowButton(props: {
|
export function FollowButton(props: {
|
||||||
isFollowing: boolean | undefined
|
isFollowing: boolean | undefined
|
||||||
|
@ -34,7 +35,7 @@ export function FollowButton(props: {
|
||||||
small && smallStyle,
|
small && smallStyle,
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
onClick={onUnfollow}
|
onClick={withTracking(onUnfollow, 'unfollow')}
|
||||||
>
|
>
|
||||||
Following
|
Following
|
||||||
</button>
|
</button>
|
||||||
|
@ -44,7 +45,7 @@ export function FollowButton(props: {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={clsx('btn btn-sm', small && smallStyle, className)}
|
className={clsx('btn btn-sm', small && smallStyle, className)}
|
||||||
onClick={onFollow}
|
onClick={withTracking(onFollow, 'follow')}
|
||||||
>
|
>
|
||||||
Follow
|
Follow
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { PencilIcon } from '@heroicons/react/outline'
|
import { PencilIcon } from '@heroicons/react/outline'
|
||||||
|
|
||||||
import { User } from 'common/user'
|
import { User } from 'common/user'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useFollowers, useFollows } from 'web/hooks/use-follows'
|
import { useFollowers, useFollows } from 'web/hooks/use-follows'
|
||||||
|
@ -10,6 +11,7 @@ import { Modal } from './layout/modal'
|
||||||
import { Tabs } from './layout/tabs'
|
import { Tabs } from './layout/tabs'
|
||||||
import { useDiscoverUsers } from 'web/hooks/use-users'
|
import { useDiscoverUsers } from 'web/hooks/use-users'
|
||||||
import { TextButton } from './text-button'
|
import { TextButton } from './text-button'
|
||||||
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function FollowingButton(props: { user: User }) {
|
export function FollowingButton(props: { user: User }) {
|
||||||
const { user } = props
|
const { user } = props
|
||||||
|
@ -48,7 +50,10 @@ export function EditFollowingButton(props: { user: User; className?: string }) {
|
||||||
className,
|
className,
|
||||||
'btn btn-sm btn-ghost cursor-pointer gap-2 whitespace-nowrap text-sm normal-case text-gray-700'
|
'btn btn-sm btn-ghost cursor-pointer gap-2 whitespace-nowrap text-sm normal-case text-gray-700'
|
||||||
)}
|
)}
|
||||||
onClick={() => setIsOpen(true)}
|
onClick={() => {
|
||||||
|
setIsOpen(true)
|
||||||
|
track('edit following button')
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<PencilIcon className="inline h-4 w-4" />
|
<PencilIcon className="inline h-4 w-4" />
|
||||||
Following
|
Following
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { Tabs } from './layout/tabs'
|
||||||
import { NoLabel, YesLabel } from './outcome-label'
|
import { NoLabel, YesLabel } from './outcome-label'
|
||||||
import { Col } from './layout/col'
|
import { Col } from './layout/col'
|
||||||
import { InfoTooltip } from './info-tooltip'
|
import { InfoTooltip } from './info-tooltip'
|
||||||
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function LiquidityPanel(props: { contract: CPMMContract }) {
|
export function LiquidityPanel(props: { contract: CPMMContract }) {
|
||||||
const { contract } = props
|
const { contract } = props
|
||||||
|
@ -57,7 +58,7 @@ export function LiquidityPanel(props: { contract: CPMMContract }) {
|
||||||
|
|
||||||
function AddLiquidityPanel(props: { contract: CPMMContract }) {
|
function AddLiquidityPanel(props: { contract: CPMMContract }) {
|
||||||
const { contract } = props
|
const { contract } = props
|
||||||
const { id: contractId } = contract
|
const { id: contractId, slug } = contract
|
||||||
|
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
|
||||||
|
@ -99,6 +100,8 @@ function AddLiquidityPanel(props: { contract: CPMMContract }) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((_) => setError('Server error'))
|
.catch((_) => setError('Server error'))
|
||||||
|
|
||||||
|
track('add liquidity', { amount, contractId, slug })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -177,6 +180,8 @@ function WithdrawLiquidityPanel(props: {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
})
|
})
|
||||||
.catch((_) => setError('Server error'))
|
.catch((_) => setError('Server error'))
|
||||||
|
|
||||||
|
track('withdraw liquidity')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSuccess)
|
if (isSuccess)
|
||||||
|
|
|
@ -17,6 +17,7 @@ import clsx from 'clsx'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import NotificationsIcon from 'web/components/notifications-icon'
|
import NotificationsIcon from 'web/components/notifications-icon'
|
||||||
import { useIsIframe } from 'web/hooks/use-is-iframe'
|
import { useIsIframe } from 'web/hooks/use-is-iframe'
|
||||||
|
import { trackCallback } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
function getNavigation(username: string) {
|
function getNavigation(username: string) {
|
||||||
return [
|
return [
|
||||||
|
@ -106,6 +107,7 @@ function NavBarItem(props: { item: Item; currentPage: string }) {
|
||||||
'block w-full py-1 px-3 text-center hover:bg-indigo-200 hover:text-indigo-700',
|
'block w-full py-1 px-3 text-center hover:bg-indigo-200 hover:text-indigo-700',
|
||||||
currentPage === item.href && 'bg-gray-200 text-indigo-700'
|
currentPage === item.href && 'bg-gray-200 text-indigo-700'
|
||||||
)}
|
)}
|
||||||
|
onClick={trackCallback('navbar: ' + item.name)}
|
||||||
>
|
>
|
||||||
<item.icon className="my-1 mx-auto h-6 w-6" />
|
<item.icon className="my-1 mx-auto h-6 w-6" />
|
||||||
{item.name}
|
{item.name}
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
||||||
import { User } from 'web/lib/firebase/users'
|
import { User } from 'web/lib/firebase/users'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney } from 'common/util/format'
|
||||||
import { Avatar } from '../avatar'
|
import { Avatar } from '../avatar'
|
||||||
|
import { trackCallback } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function ProfileSummary(props: { user: User }) {
|
export function ProfileSummary(props: { user: User }) {
|
||||||
const { user } = props
|
const { user } = props
|
||||||
return (
|
return (
|
||||||
<Link href={`/${user.username}`}>
|
<Link href={`/${user.username}`}>
|
||||||
<a className="group flex flex-row items-center gap-4 rounded-md py-3 text-gray-500 hover:bg-gray-100 hover:text-gray-700">
|
<a
|
||||||
|
onClick={trackCallback('sidebar: profile')}
|
||||||
|
className="group flex flex-row items-center gap-4 rounded-md py-3 text-gray-500 hover:bg-gray-100 hover:text-gray-700"
|
||||||
|
>
|
||||||
<Avatar avatarUrl={user.avatarUrl} username={user.username} noLink />
|
<Avatar avatarUrl={user.avatarUrl} username={user.username} noLink />
|
||||||
|
|
||||||
<div className="truncate">
|
<div className="truncate">
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { Row } from '../layout/row'
|
||||||
import NotificationsIcon from 'web/components/notifications-icon'
|
import NotificationsIcon from 'web/components/notifications-icon'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
|
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
|
||||||
|
import { trackCallback, withTracking } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
// Create an icon from the url of an image
|
// Create an icon from the url of an image
|
||||||
function IconFromUrl(url: string): React.ComponentType<{ className?: string }> {
|
function IconFromUrl(url: string): React.ComponentType<{ className?: string }> {
|
||||||
|
@ -125,6 +126,7 @@ function SidebarItem(props: { item: Item; currentPage: string }) {
|
||||||
return (
|
return (
|
||||||
<Link href={item.href} key={item.name}>
|
<Link href={item.href} key={item.name}>
|
||||||
<a
|
<a
|
||||||
|
onClick={trackCallback('sidebar: ' + item.name)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
item.href == currentPage
|
item.href == currentPage
|
||||||
? 'bg-gray-200 text-gray-900'
|
? 'bg-gray-200 text-gray-900'
|
||||||
|
@ -216,7 +218,11 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
{user && (
|
{user && (
|
||||||
<MenuButton
|
<MenuButton
|
||||||
menuItems={[
|
menuItems={[
|
||||||
{ name: 'Sign out', href: '#', onClick: () => firebaseLogout() },
|
{
|
||||||
|
name: 'Sign out',
|
||||||
|
href: '#',
|
||||||
|
onClick: withTracking(firebaseLogout, 'sign out'),
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
buttonContent={<MoreButton />}
|
buttonContent={<MoreButton />}
|
||||||
/>
|
/>
|
||||||
|
@ -237,13 +243,16 @@ export default function Sidebar(props: { className?: string }) {
|
||||||
<div className={'aligncenter flex justify-center'}>
|
<div className={'aligncenter flex justify-center'}>
|
||||||
{user ? (
|
{user ? (
|
||||||
<Link href={'/create'} passHref>
|
<Link href={'/create'} passHref>
|
||||||
<button className={clsx(gradient, buttonStyle)}>
|
<button
|
||||||
|
className={clsx(gradient, buttonStyle)}
|
||||||
|
onClick={trackCallback('create question button')}
|
||||||
|
>
|
||||||
Create a question
|
Create a question
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={firebaseLogin}
|
onClick={withTracking(firebaseLogin, 'sign in')}
|
||||||
className={clsx(gradient, buttonStyle)}
|
className={clsx(gradient, buttonStyle)}
|
||||||
>
|
>
|
||||||
Sign in
|
Sign in
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import React, { Fragment } from 'react'
|
import React, { Fragment } from 'react'
|
||||||
import { CodeIcon } from '@heroicons/react/outline'
|
import { CodeIcon } from '@heroicons/react/outline'
|
||||||
import { Menu, Transition } from '@headlessui/react'
|
import { Menu, Transition } from '@headlessui/react'
|
||||||
|
|
||||||
import { Contract } from 'common/contract'
|
import { Contract } from 'common/contract'
|
||||||
import { contractPath } from 'web/lib/firebase/contracts'
|
import { contractPath } from 'web/lib/firebase/contracts'
|
||||||
import { DOMAIN } from 'common/envs/constants'
|
import { DOMAIN } from 'common/envs/constants'
|
||||||
import { copyToClipboard } from 'web/lib/util/copy'
|
import { copyToClipboard } from 'web/lib/util/copy'
|
||||||
import { ToastClipboard } from 'web/components/toast-clipboard'
|
import { ToastClipboard } from 'web/components/toast-clipboard'
|
||||||
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
function copyEmbedCode(contract: Contract) {
|
function copyEmbedCode(contract: Contract) {
|
||||||
const title = contract.question
|
const title = contract.question
|
||||||
|
@ -26,7 +28,10 @@ export function ShareEmbedButton(props: {
|
||||||
<Menu
|
<Menu
|
||||||
as="div"
|
as="div"
|
||||||
className="relative z-10 flex-shrink-0"
|
className="relative z-10 flex-shrink-0"
|
||||||
onMouseUp={() => copyEmbedCode(contract)}
|
onMouseUp={() => {
|
||||||
|
copyEmbedCode(contract)
|
||||||
|
track('copy embed code')
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Menu.Button
|
<Menu.Button
|
||||||
className="btn btn-xs normal-case"
|
className="btn btn-xs normal-case"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { firebaseLogin } from 'web/lib/firebase/users'
|
import { firebaseLogin } from 'web/lib/firebase/users'
|
||||||
|
import { withTracking } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function SignUpPrompt() {
|
export function SignUpPrompt() {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
@ -8,7 +9,7 @@ export function SignUpPrompt() {
|
||||||
return user === null ? (
|
return user === null ? (
|
||||||
<button
|
<button
|
||||||
className="btn flex-1 whitespace-nowrap border-none bg-gradient-to-r from-teal-500 to-green-500 px-10 text-lg font-medium normal-case hover:from-teal-600 hover:to-green-600"
|
className="btn flex-1 whitespace-nowrap border-none bg-gradient-to-r from-teal-500 to-green-500 px-10 text-lg font-medium normal-case hover:from-teal-600 hover:to-green-600"
|
||||||
onClick={firebaseLogin}
|
onClick={withTracking(firebaseLogin, 'sign up to bet')}
|
||||||
>
|
>
|
||||||
Sign up to bet!
|
Sign up to bet!
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { Col } from './layout/col'
|
||||||
import { Row } from './layout/row'
|
import { Row } from './layout/row'
|
||||||
import { TagsList } from './tags-list'
|
import { TagsList } from './tags-list'
|
||||||
import { MAX_TAG_LENGTH } from 'common/contract'
|
import { MAX_TAG_LENGTH } from 'common/contract'
|
||||||
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function TagsInput(props: { contract: Contract; className?: string }) {
|
export function TagsInput(props: { contract: Contract; className?: string }) {
|
||||||
const { contract, className } = props
|
const { contract, className } = props
|
||||||
|
@ -24,6 +25,7 @@ export function TagsInput(props: { contract: Contract; className?: string }) {
|
||||||
})
|
})
|
||||||
setIsSubmitting(false)
|
setIsSubmitting(false)
|
||||||
setTagText('')
|
setTagText('')
|
||||||
|
track('save tags')
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
import { trackCallback } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export function TweetButton(props: { className?: string; tweetText: string }) {
|
export function TweetButton(props: { className?: string; tweetText: string }) {
|
||||||
const { tweetText, className } = props
|
const { tweetText, className } = props
|
||||||
|
@ -12,6 +13,7 @@ export function TweetButton(props: { className?: string; tweetText: string }) {
|
||||||
color: '#1da1f2',
|
color: '#1da1f2',
|
||||||
}}
|
}}
|
||||||
href={getTweetHref(tweetText)}
|
href={getTweetHref(tweetText)}
|
||||||
|
onClick={trackCallback('share tweet')}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<img className="mr-2" src={'/twitter-logo.svg'} width={15} height={15} />
|
<img className="mr-2" src={'/twitter-logo.svg'} width={15} height={15} />
|
||||||
|
|
8
web/hooks/use-tracking.ts
Normal file
8
web/hooks/use-tracking.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { track } from '@amplitude/analytics-browser'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
|
export const useTracking = (eventName: string, eventProperties?: any) => {
|
||||||
|
useEffect(() => {
|
||||||
|
track(eventName, eventProperties)
|
||||||
|
}, [])
|
||||||
|
}
|
|
@ -12,15 +12,24 @@ init(ENV_CONFIG.amplitudeApiKey ?? '', undefined, { includeReferrer: true })
|
||||||
|
|
||||||
export { track }
|
export { track }
|
||||||
|
|
||||||
// convenience function
|
// Convenience functions:
|
||||||
|
|
||||||
|
export const trackCallback =
|
||||||
|
(eventName: string, eventProperties?: any) => () => {
|
||||||
|
track(eventName, eventProperties)
|
||||||
|
}
|
||||||
|
|
||||||
export const withTracking =
|
export const withTracking =
|
||||||
(
|
(
|
||||||
f: (() => void) | (() => Promise<void>),
|
f: (() => void) | (() => Promise<void>),
|
||||||
eventName: string,
|
eventName: string,
|
||||||
eventProperties?: any
|
eventProperties?: any
|
||||||
) =>
|
) =>
|
||||||
() =>
|
async () => {
|
||||||
Promise.all([f(), track(eventName, eventProperties).promise])
|
const promise = f()
|
||||||
|
track(eventName, eventProperties)
|
||||||
|
await promise
|
||||||
|
}
|
||||||
|
|
||||||
export async function identifyUser(userId: string) {
|
export async function identifyUser(userId: string) {
|
||||||
setUserId(userId)
|
setUserId(userId)
|
||||||
|
|
|
@ -40,6 +40,7 @@ import { useIsIframe } from 'web/hooks/use-is-iframe'
|
||||||
import ContractEmbedPage from '../embed/[username]/[contractSlug]'
|
import ContractEmbedPage from '../embed/[username]/[contractSlug]'
|
||||||
import { useBets } from 'web/hooks/use-bets'
|
import { useBets } from 'web/hooks/use-bets'
|
||||||
import { AlertBox } from 'web/components/alert-box'
|
import { AlertBox } from 'web/components/alert-box'
|
||||||
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
|
|
||||||
export const getStaticProps = fromPropz(getStaticPropz)
|
export const getStaticProps = fromPropz(getStaticPropz)
|
||||||
export async function getStaticPropz(props: {
|
export async function getStaticPropz(props: {
|
||||||
|
@ -108,6 +109,12 @@ export function ContractPageContent(
|
||||||
|
|
||||||
const contract = useContractWithPreload(props.contract) ?? props.contract
|
const contract = useContractWithPreload(props.contract) ?? props.contract
|
||||||
|
|
||||||
|
useTracking('view market', {
|
||||||
|
slug: contract.slug,
|
||||||
|
contractId: contract.id,
|
||||||
|
creatorId: contract.creatorId,
|
||||||
|
})
|
||||||
|
|
||||||
const bets = useBets(contract.id) ?? props.bets
|
const bets = useBets(contract.id) ?? props.bets
|
||||||
// Sort for now to see if bug is fixed.
|
// Sort for now to see if bug is fixed.
|
||||||
comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
|
comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { getUserByUsername, User } from 'web/lib/firebase/users'
|
||||||
import { UserPage } from 'web/components/user-page'
|
import { UserPage } from 'web/components/user-page'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import Custom404 from '../404'
|
import Custom404 from '../404'
|
||||||
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
|
|
||||||
export default function UserProfile(props: {
|
export default function UserProfile(props: {
|
||||||
tab?: 'markets' | 'comments' | 'bets'
|
tab?: 'markets' | 'comments' | 'bets'
|
||||||
|
@ -12,6 +13,7 @@ export default function UserProfile(props: {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [user, setUser] = useState<User | null | 'loading'>('loading')
|
const [user, setUser] = useState<User | null | 'loading'>('loading')
|
||||||
const { username } = router.query as { username: string }
|
const { username } = router.query as { username: string }
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (username) {
|
if (username) {
|
||||||
getUserByUsername(username).then(setUser)
|
getUserByUsername(username).then(setUser)
|
||||||
|
@ -20,6 +22,8 @@ export default function UserProfile(props: {
|
||||||
|
|
||||||
const currentUser = useUser()
|
const currentUser = useUser()
|
||||||
|
|
||||||
|
useTracking('view user profile', { username })
|
||||||
|
|
||||||
if (user === 'loading') return <></>
|
if (user === 'loading') return <></>
|
||||||
|
|
||||||
return user ? (
|
return user ? (
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { FundsSelector } from 'web/components/yes-no-selector'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { checkoutURL } from 'web/lib/service/stripe'
|
import { checkoutURL } from 'web/lib/service/stripe'
|
||||||
import { Page } from 'web/components/page'
|
import { Page } from 'web/components/page'
|
||||||
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
|
import { trackCallback } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
export default function AddFundsPage() {
|
export default function AddFundsPage() {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
@ -14,6 +16,8 @@ export default function AddFundsPage() {
|
||||||
2500
|
2500
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useTracking('view add funds')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<SEO
|
<SEO
|
||||||
|
@ -59,6 +63,7 @@ export default function AddFundsPage() {
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-primary w-full bg-gradient-to-r from-indigo-500 to-blue-500 font-medium hover:from-indigo-600 hover:to-blue-600"
|
className="btn btn-primary w-full bg-gradient-to-r from-indigo-500 to-blue-500 font-medium hover:from-indigo-600 hover:to-blue-600"
|
||||||
|
onClick={trackCallback('checkout', { amount: amountSelected })}
|
||||||
>
|
>
|
||||||
Checkout
|
Checkout
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { getAllCharityTxns } from 'web/lib/firebase/txns'
|
||||||
import { manaToUSD } from 'common/util/format'
|
import { manaToUSD } from 'common/util/format'
|
||||||
import { quadraticMatches } from 'common/quadratic-funding'
|
import { quadraticMatches } from 'common/quadratic-funding'
|
||||||
import { Txn } from 'common/txn'
|
import { Txn } from 'common/txn'
|
||||||
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const txns = await getAllCharityTxns()
|
const txns = await getAllCharityTxns()
|
||||||
|
@ -95,6 +96,8 @@ export default function Charity(props: {
|
||||||
[charities, query]
|
[charities, query]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useTracking('view charity')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<Col className="w-full rounded px-4 py-6 sm:px-8 xl:w-[125%]">
|
<Col className="w-full rounded px-4 py-6 sm:px-8 xl:w-[125%]">
|
||||||
|
|
|
@ -22,11 +22,14 @@ import { removeUndefinedProps } from 'common/util/object'
|
||||||
import { CATEGORIES } from 'common/categories'
|
import { CATEGORIES } from 'common/categories'
|
||||||
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
|
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
import { useWarnUnsavedChanges } from 'web/hooks/use-warn-unsaved-changes'
|
import { useWarnUnsavedChanges } from 'web/hooks/use-warn-unsaved-changes'
|
||||||
|
|
||||||
export default function Create() {
|
export default function Create() {
|
||||||
const [question, setQuestion] = useState('')
|
const [question, setQuestion] = useState('')
|
||||||
|
|
||||||
|
useTracking('view create page')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<div className="mx-auto w-full max-w-2xl">
|
<div className="mx-auto w-full max-w-2xl">
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { ContractSearch } from 'web/components/contract-search'
|
||||||
import { Contract } from 'common/contract'
|
import { Contract } from 'common/contract'
|
||||||
import { ContractPageContent } from './[username]/[contractSlug]'
|
import { ContractPageContent } from './[username]/[contractSlug]'
|
||||||
import { getContractFromSlug } from 'web/lib/firebase/contracts'
|
import { getContractFromSlug } from 'web/lib/firebase/contracts'
|
||||||
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
@ -16,6 +18,8 @@ const Home = () => {
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
useTracking('view home')
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
Router.replace('/')
|
Router.replace('/')
|
||||||
return <></>
|
return <></>
|
||||||
|
@ -42,7 +46,10 @@ const Home = () => {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="fixed bottom-[70px] right-3 inline-flex items-center rounded-full border border-transparent bg-indigo-600 p-3 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 lg:hidden"
|
className="fixed bottom-[70px] right-3 inline-flex items-center rounded-full border border-transparent bg-indigo-600 p-3 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 lg:hidden"
|
||||||
onClick={() => router.push('/create')}
|
onClick={() => {
|
||||||
|
router.push('/create')
|
||||||
|
track('mobile create button')
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<PlusSmIcon className="h-8 w-8" aria-hidden="true" />
|
<PlusSmIcon className="h-8 w-8" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Page } from 'web/components/page'
|
||||||
import { getTopCreators, getTopTraders, User } from 'web/lib/firebase/users'
|
import { getTopCreators, getTopTraders, User } from 'web/lib/firebase/users'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney } from 'common/util/format'
|
||||||
import { fromPropz, usePropz } from 'web/hooks/use-propz'
|
import { fromPropz, usePropz } from 'web/hooks/use-propz'
|
||||||
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
|
|
||||||
export const getStaticProps = fromPropz(getStaticPropz)
|
export const getStaticProps = fromPropz(getStaticPropz)
|
||||||
export async function getStaticPropz() {
|
export async function getStaticPropz() {
|
||||||
|
@ -32,6 +33,8 @@ export default function Leaderboards(props: {
|
||||||
}
|
}
|
||||||
const { topTraders, topCreators } = props
|
const { topTraders, topCreators } = props
|
||||||
|
|
||||||
|
useTracking('view leaderboards')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page margin>
|
<Page margin>
|
||||||
<Col className="items-center gap-10 lg:flex-row">
|
<Col className="items-center gap-10 lg:flex-row">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user