Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Posthog to storage #2028

Merged
merged 5 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions packages/storage-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { StorageProvider } from "./Contexts/StorageContext"
import { UserProvider } from "./Contexts/UserContext"
import { BillingProvider } from "./Contexts/BillingContext"
import { NotificationsProvider } from "./Contexts/NotificationsContext"
import { PosthogProvider } from "./Contexts/PosthogContext"

if (
process.env.NODE_ENV === "production" &&
Expand Down Expand Up @@ -122,9 +123,11 @@ const App = () => {
<Router>
<NotificationsProvider>
<BillingProvider>
<AppWrapper>
<StorageRoutes />
</AppWrapper>
<PosthogProvider>
<AppWrapper>
<StorageRoutes />
</AppWrapper>
</PosthogProvider>
</BillingProvider>
</NotificationsProvider>
</Router>
Expand Down
2 changes: 2 additions & 0 deletions packages/storage-ui/src/Components/Pages/BillingHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Typography } from "@chainsafe/common-components"
import { Trans } from "@lingui/macro"
import InvoiceLines from "../Elements/InvoiceLines"
import PayInvoiceModal from "../Modules/SubscriptionTab/PayInvoice/PayInvoiceModal"
import { usePageTrack } from "../../Contexts/PosthogContext"

const useStyles = makeStyles(
({ constants, breakpoints }: CSSTheme) =>
Expand Down Expand Up @@ -32,6 +33,7 @@ const useStyles = makeStyles(
const BillingHistory = () => {
const classes = useStyles()
const [invoiceToPay, setInvoiceToPay] = useState<string | undefined>()
usePageTrack()
FSM1 marked this conversation as resolved.
Show resolved Hide resolved

return (
<div className={classes.root}>
Expand Down
2 changes: 2 additions & 0 deletions packages/storage-ui/src/Components/Pages/BucketPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { FileBrowserContext, ISelectedFile } from "../../Contexts/FileBrowserCon
import { parseFileContentResponse } from "../../Utils/Helpers"
import { useLocalStorage } from "@chainsafe/browser-storage-hooks"
import { DISMISSED_SURVEY_KEY } from "../Modules/SurveyBanner"
import { usePageTrack } from "../../Contexts/PosthogContext"

const BucketPage: React.FC<IFileBrowserModuleProps> = () => {
const { storageBuckets, uploadFiles, uploadsInProgress, getStorageSummary, downloadFile } = useStorage()
Expand All @@ -30,6 +31,7 @@ const BucketPage: React.FC<IFileBrowserModuleProps> = () => {
const { localStorageGet, localStorageSet } = useLocalStorage()
const showSurvey = localStorageGet(DISMISSED_SURVEY_KEY) === "false"
const { pathname } = useLocation()
usePageTrack()
FSM1 marked this conversation as resolved.
Show resolved Hide resolved

const bucketId = useMemo(() =>
pathname.split("/")[2]
Expand Down
3 changes: 3 additions & 0 deletions packages/storage-ui/src/Components/Pages/BucketsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import CustomModal from "../Elements/CustomModal"
import { Form, FormikProvider, useFormik } from "formik"
import { bucketNameValidator } from "../../Utils/validationSchema"
import { useCallback } from "react"
import { usePageTrack } from "../../Contexts/PosthogContext"

export const desktopGridSettings = "3fr 190px 70px !important"
export const mobileGridSettings = "3fr 190px 70px !important"
Expand Down Expand Up @@ -122,6 +123,8 @@ const BucketsPage = () => {
, [bucketsToShow]
)

usePageTrack()

useEffect(() => {
// this is needed for tests
refreshBuckets()
Expand Down
2 changes: 2 additions & 0 deletions packages/storage-ui/src/Components/Pages/CidsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import CidRow from "../Elements/CidRow"
import { CSSTheme } from "../../Themes/types"
import AddCIDModal from "../Modules/AddCIDModal"
import { PinStatus } from "@chainsafe/files-api-client"
import { usePageTrack } from "../../Contexts/PosthogContext"

export const desktopGridSettings = "3fr 180px 120px 120px 140px 70px !important"
export const mobileGridSettings = "3fr 180px 120px 120px 140px 70px !important"
Expand Down Expand Up @@ -59,6 +60,7 @@ const CidsPage = () => {
const [addCIDOpen, setAddCIDOpen] = useState(false)
const [sortColumn, setSortColumn] = useState<SortColumn>("date_uploaded")
const [sortDirection, setSortDirection] = useState<SortDirection>("descend")
usePageTrack()

const handleSortToggle = (
targetColumn: SortColumn
Expand Down
2 changes: 2 additions & 0 deletions packages/storage-ui/src/Components/Pages/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import BottomDarkSVG from "../../Media/landing/layers/dark/Bottom.dark.svg"
import TopDarkSVG from "../../Media/landing/layers/dark/Top.dark.svg"
import BottomLightSVG from "../../Media/landing/layers/light/Bottom.light.svg"
import TopLightSVG from "../../Media/landing/layers/light/Top.light.svg"
import { usePageTrack } from "../../Contexts/PosthogContext"


const useStyles = makeStyles(
Expand Down Expand Up @@ -122,6 +123,7 @@ const useStyles = makeStyles(
const LoginPage = () => {
const classes = useStyles()
const { themeKey } = useThemeSwitcher()
usePageTrack()

return (
<div className={classes.root}>
Expand Down
2 changes: 2 additions & 0 deletions packages/storage-ui/src/Components/Pages/SettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import clsx from "clsx"
import ApiKeys from "../Modules/ApiKeys"
import { useBilling } from "../../Contexts/BillingContext"
import SubscriptionTab from "../Modules/SubscriptionTab"
import { usePageTrack } from "../../Contexts/PosthogContext"

const TabPane = (props: ITabPaneProps<SettingsPath>) => TabPaneOrigin(props)
const useStyles = makeStyles(({ constants, breakpoints, palette }: ITheme) =>
Expand Down Expand Up @@ -130,6 +131,7 @@ const SettingsPage: React.FC = () => {
const classes = useStyles()
const { redirect } = useHistory()
const { isBillingEnabled } = useBilling()
usePageTrack()

const onSelectTab = useCallback(
(path: SettingsPath) => redirect(ROUTE_LINKS.SettingsPath(path))
Expand Down
202 changes: 202 additions & 0 deletions packages/storage-ui/src/Contexts/PosthogContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import React, { useCallback, useEffect, useMemo, useState } from "react"
import posthog from "posthog-js"
import { Button, Typography, useLocation } from "@chainsafe/common-components"
import { createStyles, makeStyles } from "@chainsafe/common-theme"
import { Trans } from "@lingui/macro"
import { useLocalStorage } from "@chainsafe/browser-storage-hooks"
import { CSSTheme } from "../Themes/types"
import { useUser } from "./UserContext"

export type PosthogContext = {
hasOptedIn: boolean
posthogInitialized: boolean
captureEvent: (eventName: string, properties?: posthog.Properties) => void
}

type PosthogProviderProps = posthog.Config & {
children: React.ReactNode | React.ReactNode[]
}

const PosthogContext = React.createContext<PosthogContext>({
hasOptedIn: false,
posthogInitialized: false,
captureEvent: () => undefined
})

const useStyles = makeStyles(
({ palette, breakpoints, constants, zIndex }: CSSTheme) => {
return createStyles({
cookieBanner: {
position: "fixed",
bottom: 0,
width: "100%",
display: "flex",
color: palette.common.white.main,
flexDirection: "column",
backgroundColor: constants.cookieBanner.backgroundColor,
padding: "16px 32px",
zIndex: zIndex?.layer1,
[breakpoints.down("sm")]: {
padding: "16px 16px"
}
},
bannerHeading: {
fontSize: 24,
lineHeight: "42px",
[breakpoints.down("sm")]: {
fontSize: 22,
lineHeight: "40px"
}
},
bannerText: {
fontSize: 14,
lineHeight: "18px",
marginBottom: constants.generalUnit * 1.5,
[breakpoints.down("sm")]: {
fontSize: 13,
lineHeight: "16px"
}
},
link: {
color: palette.common.white.main,
paddingLeft: constants.generalUnit
},
buttonSection: {
display: "flex",
flexDirection: "row",
margin: `${constants.generalUnit}px 0`
},
acceptButton: {
marginLeft: constants.generalUnit * 2
}
})
}
)

const TOUCHED_COOKIE_BANNER_KEY = "css.touchedCookieBanner"

const PosthogProvider = ({ children }: PosthogProviderProps) => {
const [hasOptedIn, setHasOptedIn] = useState(false)
const [showBanner, setShowBanner] = useState(false)
const [hasTouchedCookieBanner, setHasTouchedCookieBanner ] = useState(false)
const { localStorageGet, localStorageSet } = useLocalStorage()
const { profile } = useUser()

const classes = useStyles()
const posthogInitialized = useMemo(() =>
!!process.env.REACT_APP_POSTHOG_PROJECT_API_KEY &&
!!process.env.REACT_APP_POSTHOG_INSTANCE_ADDRESS,
[])

useEffect(() => {
if(localStorageGet(TOUCHED_COOKIE_BANNER_KEY) === null){
localStorageSet(TOUCHED_COOKIE_BANNER_KEY, "false")
}
}, [localStorageGet, localStorageSet])

useEffect(() => {
if(posthogInitialized && !hasTouchedCookieBanner && localStorageGet(TOUCHED_COOKIE_BANNER_KEY) === "false"){
setShowBanner(true)
} else {
setShowBanner(false)
}
}, [posthogInitialized, hasTouchedCookieBanner, localStorageGet])

const touchCookieBanner = useCallback(() => {
localStorageSet(TOUCHED_COOKIE_BANNER_KEY, "true")
setHasTouchedCookieBanner(true)
}, [localStorageSet])

const optInCapturing = useCallback(() => {
if (posthogInitialized) {
posthog.opt_in_capturing()
touchCookieBanner()
setHasOptedIn(true)
}
}, [posthogInitialized, touchCookieBanner])

const optOutCapturing = useCallback(() => {
if (posthogInitialized) {
posthog.opt_out_capturing()
touchCookieBanner()
}
}, [posthogInitialized, touchCookieBanner])

const captureEvent = useCallback((eventName: string, properties?: posthog.Properties) => {
if (posthogInitialized) {
posthog.capture(eventName, properties)
}
}, [posthogInitialized])

useEffect(() => {
if (profile) {
posthogInitialized && posthog.identify(profile.userId)
posthogInitialized && posthog.capture("Logged In", { userId: profile.userId })
} else {
posthogInitialized && posthog.reset()
}
}, [profile, posthogInitialized])

return (
<PosthogContext.Provider
value={{
hasOptedIn,
posthogInitialized,
captureEvent
}}
>
{children}
{showBanner &&
<div className={classes.cookieBanner}>
<Typography className={classes.bannerHeading}><Trans>This website uses cookies</Trans></Typography>
<Typography className={classes.bannerText}>
<Trans>
This website uses cookies that help the website function and track interactions for analytics purposes.
You have the right to decline our use of cookies. For us to provide a customizable user experience to you,
please click on the Accept button below.
<a
className={classes.link}
href="https://files.chainsafe.io/privacy-policy"
target='_blank'
rel='noopener noreferrer'>Learn more
</a>
</Trans>
</Typography>
<div className={classes.buttonSection}>
<Button
onClick={optOutCapturing}
variant='secondary'
>
<Trans>Decline</Trans></Button>
<Button
onClick={optInCapturing}
variant='outline'
className={classes.acceptButton}
>
<Trans>Accept</Trans>
</Button>
</div>
</div>
}
</PosthogContext.Provider>
)
}

function usePosthogContext() {
const context = React.useContext(PosthogContext)
if (context === undefined) {
throw new Error("usePosthogContext must be used within a LanguageProvider")
}
return context
}

function usePageTrack() {
const { pathname } = useLocation()
const { hasOptedIn, posthogInitialized } = usePosthogContext()

useEffect(() => {
posthogInitialized && hasOptedIn && posthog.capture("$pageview")
}, [pathname, hasOptedIn, posthogInitialized])
}

export { PosthogProvider, usePosthogContext, usePageTrack }
3 changes: 3 additions & 0 deletions packages/storage-ui/src/Themes/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,7 @@ export interface CsSColors extends IConstants {
surveyBanner: {
color: string
}
cookieBanner: {
backgroundColor: string
}
}
3 changes: 3 additions & 0 deletions packages/storage-ui/src/Themes/DarkTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,9 @@ export const darkTheme = createTheme<CsSColors>({
color: "#DBDBDB",
shadow: "0px 0px 4px rgba(24, 144, 255, 0.5)",
placeholderColor: "#595959"
},
cookieBanner: {
backgroundColor: "var(--gray9)"
}
} as CsSColors)
},
Expand Down
3 changes: 3 additions & 0 deletions packages/storage-ui/src/Themes/LightTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ export const lightTheme = createTheme<CsSColors>({
color: "#595959",
shadow: "0px 0px 4px rgba(24, 144, 255, 0.5)",
placeholderColor: "#BFBFBF"
},
cookieBanner: {
backgroundColor: "var(--gray9)"
}
} as CsSColors)
},
Expand Down
9 changes: 9 additions & 0 deletions packages/storage-ui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import ReactDOM from "react-dom"
import "./index.css"
import App from "./App"
import * as serviceWorker from "./serviceWorker"
import posthog from "posthog-js"

if (process.env.REACT_APP_POSTHOG_PROJECT_API_KEY &&
process.env.REACT_APP_POSTHOG_INSTANCE_ADDRESS) {
posthog.init(process.env.REACT_APP_POSTHOG_PROJECT_API_KEY, {
api_host: process.env.REACT_APP_POSTHOG_INSTANCE_ADDRESS,
opt_out_capturing_by_default: true
})
}

ReactDOM.render(<App />, document.getElementById("root"))

Expand Down
Loading