Merge branch 'main' into humbug-integration

pull/220/head
kompotkot 2021-09-02 09:06:50 +00:00
commit 120b6a952c
20 zmienionych plików z 299 dodań i 166 usunięć

Wyświetl plik

@ -10,23 +10,20 @@ import (
"encoding/json"
"flag"
"fmt"
"math/big"
"os"
"time"
humbug "github.com/bugout-dev/humbug/go/pkg"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/google/uuid"
)
// Generate humbug client to be able write data in Bugout journal.
func humbugClientFromEnv() (*humbug.HumbugReporter, error) {
clientID := os.Getenv("ETHTXPOOL_HUMBUG_CLIENT_ID")
humbugToken := os.Getenv("ETHTXPOOL_HUMBUG_TOKEN")
sessionID := uuid.New().String()
// Generate humbug client
func humbugClient(sessionID string, clientID string, humbugToken string) (*humbug.HumbugReporter, error) {
consent := humbug.CreateHumbugConsent(humbug.True)
reporter, err := humbug.CreateHumbugReporter(consent, clientID, sessionID, humbugToken)
return reporter, err
@ -124,11 +121,6 @@ func PollTxpoolContent(gethClient *rpc.Client, interval int, reporter *humbug.Hu
continue
}
// TODO(kompotkot, zomglings): Humbug API (on Spire) support bulk publication of reports. We should modify
// Humbug go client to use the bulk publish endpoint. Currently, if we have to publish all transactions
// pending in txpool, we *will* get rate limited. We may want to consider adding a publisher to the
// Humbug go client that can listen on a channel and will handle rate limiting, bulk publication etc. itself
// (without user having to worry about it).
ReportTitle := "Ethereum: Pending transaction: " + transactionHash.String()
ReportTags := []string{
"hash:" + transactionHash.String(),
@ -138,6 +130,7 @@ func PollTxpoolContent(gethClient *rpc.Client, interval int, reporter *humbug.Hu
fmt.Sprintf("max_priority_fee_per_gas:%d", pendingTx.Transaction.MaxPriorityFeePerGas.ToInt()),
fmt.Sprintf("max_fee_per_gas:%d", pendingTx.Transaction.MaxFeePerGas.ToInt()),
fmt.Sprintf("gas:%d", pendingTx.Transaction.Gas),
fmt.Sprintf("value:%d", new(big.Float).Quo(new(big.Float).SetInt(transaction.Value.ToInt()), big.NewFloat(params.Ether))),
"crawl_type:ethereum_txpool",
}
report := humbug.Report{
@ -188,6 +181,23 @@ func main() {
flag.IntVar(&intervalSeconds, "interval", 1, "Number of seconds to wait between RPC calls to query the transaction pool (default: 1)")
flag.Parse()
sessionID := uuid.New().String()
// Humbug crash client to collect errors
crashReporter, err := humbugClient(sessionID, "moonstream-crawlers", os.Getenv("HUMBUG_REPORTER_CRAWLERS_TOKEN"))
if err != nil {
panic(fmt.Sprintf("Invalid Humbug Crash configuration: %s", err.Error()))
}
crashReporter.Publish(humbug.SystemReport())
defer func() {
message := recover()
if message != nil {
fmt.Printf("Error: %s\n", message)
crashReporter.Publish(humbug.PanicReport(message))
}
}()
// Set connection with Ethereum blockchain via geth
gethClient, err := rpc.Dial(gethConnectionString)
if err != nil {
@ -195,7 +205,8 @@ func main() {
}
defer gethClient.Close()
reporter, err := humbugClientFromEnv()
// Humbug client to be able write data in Bugout journal
reporter, err := humbugClient(sessionID, os.Getenv("ETHTXPOOL_HUMBUG_CLIENT_ID"), os.Getenv("ETHTXPOOL_HUMBUG_TOKEN"))
if err != nil {
panic(fmt.Sprintf("Invalid Humbug configuration: %s", err.Error()))
}

Wyświetl plik

@ -1,2 +1,3 @@
export ETHTXPOOL_HUMBUG_CLIENT_ID="<client id for the crawling machine>"
export ETHTXPOOL_HUMBUG_TOKEN="<Generate an integration and a Humbug token from https://bugout.dev/account/teams>"
export HUMBUG_REPORTER_CRAWLERS_TOKEN="<Bugout Humbug token for crash reports>"

Wyświetl plik

@ -0,0 +1,7 @@
from .reporter import reporter
from .version import MOONCRAWL_VERSION
# Reporting
reporter.tags.append(f"version:{MOONCRAWL_VERSION}")
reporter.system_report(publish=True)
reporter.setup_excepthook(publish=True)

Wyświetl plik

@ -48,7 +48,7 @@ def yield_blocks_numbers_lists(
print(
"Wrong format provided, expected {bottom_block}-{top_block}, as ex. 105-340"
)
return
raise Exception
starting_block = max(input_start_block, input_end_block)
ending_block = min(input_start_block, input_end_block)

Wyświetl plik

@ -0,0 +1,18 @@
import uuid
from humbug.consent import HumbugConsent
from humbug.report import HumbugReporter
from .settings import HUMBUG_REPORTER_CRAWLERS_TOKEN
session_id = str(uuid.uuid4())
client_id = "moonstream-crawlers"
reporter = HumbugReporter(
name="moonstream-crawlers",
consent=HumbugConsent(True),
client_id=client_id,
session_id=session_id,
bugout_token=HUMBUG_REPORTER_CRAWLERS_TOKEN,
tags=[],
)

Wyświetl plik

@ -1,5 +1,9 @@
import os
# Bugout
HUMBUG_REPORTER_CRAWLERS_TOKEN = os.environ.get("HUMBUG_REPORTER_CRAWLERS_TOKEN")
# Geth
MOONSTREAM_IPC_PATH = os.environ.get("MOONSTREAM_IPC_PATH", None)
MOONSTREAM_CRAWL_WORKERS = 4
@ -12,5 +16,5 @@ except:
f"Could not parse MOONSTREAM_CRAWL_WORKERS as int: {MOONSTREAM_CRAWL_WORKERS_RAW}"
)
# Etherscan
MOONSTREAM_ETHERSCAN_TOKEN = os.environ.get("MOONSTREAM_ETHERSCAN_TOKEN")

Wyświetl plik

@ -6,3 +6,4 @@ export MOONSTREAM_ETHERSCAN_TOKEN="<Token for etherscan>"
export AWS_S3_SMARTCONTRACT_BUCKET="<AWS S3 bucket for smart contracts>"
export MOONSTREAM_HUMBUG_TOKEN="<Token for crawlers store data via Humbug>"
export COINMARKETCAP_API_KEY="<API key to parse conmarketcap>"
export HUMBUG_REPORTER_CRAWLERS_TOKEN="<Bugout Humbug token for crash reports>"

Wyświetl plik

@ -1,6 +1,5 @@
from setuptools import find_packages, setup
from mooncrawl.version import MOONCRAWL_VERSION
long_description = ""
with open("README.md") as ifp:
@ -8,7 +7,7 @@ with open("README.md") as ifp:
setup(
name="mooncrawl",
version=MOONCRAWL_VERSION,
version="0.0.3",
author="Bugout.dev",
author_email="engineers@bugout.dev",
license="Apache License 2.0",
@ -34,6 +33,7 @@ setup(
zip_safe=False,
install_requires=[
"moonstreamdb @ git+https://git@github.com/bugout-dev/moonstream.git@39d2b8e36a49958a9ae085ec2cc1be3fc732b9d0#egg=moonstreamdb&subdirectory=db",
"humbug",
"python-dateutil",
"requests",
"tqdm",

Wyświetl plik

@ -24,12 +24,15 @@ import {
} from "@chakra-ui/react";
import dynamic from "next/dynamic";
import useUser from "../src/core/hooks/useUser";
import useAnalytics from "../src/core/hooks/useAnalytics";
import useModals from "../src/core/hooks/useModals";
import useRouter from "../src/core/hooks/useRouter";
import { MIXPANEL_PROPS } from "../src/core/providers/AnalyticsProvider/constants";
import {
MIXPANEL_PROPS,
MIXPANEL_EVENTS,
} from "../src/core/providers/AnalyticsProvider/constants";
import UIContext from "../src/core/providers/UIProvider/context";
import { AWS_ASSETS_PATH } from "../src/core/constants";
import mixpanel from "mixpanel-browser";
const SplitWithImage = dynamic(
() => import("../src/components/SplitWithImage"),
{
@ -105,7 +108,6 @@ const Homepage = () => {
const router = useRouter();
const { isInit } = useUser();
const { MIXPANEL_EVENTS, track } = useAnalytics();
const { toggleModal } = useModals();
const [
isLargerThan720px,
@ -379,27 +381,30 @@ const Homepage = () => {
label: "Crypto trader",
link: "/#cryptoTrader",
onClick: () => {
track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_CLICKED}`]: `scroll to CryptoTrader`,
});
mixpanel.get_distinct_id() &&
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `scroll to CryptoTrader`,
});
},
}}
button2={{
label: "Algorithmic Fund",
link: "/#algoFund",
onClick: () => {
track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_CLICKED}`]: `scroll to AlgoFund`,
});
mixpanel.get_distinct_id() &&
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `scroll to AlgoFund`,
});
},
}}
button3={{
label: "Developer",
link: "/#smartDeveloper",
onClick: () => {
track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_CLICKED}`]: `scroll to Developer`,
});
mixpanel.get_distinct_id() &&
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `scroll to Developer`,
});
},
}}
/>
@ -417,9 +422,10 @@ const Homepage = () => {
cta={{
label: "I want early access!",
onClick: () => {
track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_CLICKED}`]: `Early access CTA: Crypto trader`,
});
mixpanel.get_distinct_id() &&
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Early access CTA: Crypto trader`,
});
toggleModal("hubspot-trader");
},
}}
@ -464,9 +470,10 @@ const Homepage = () => {
cta={{
label: "I want early access!",
onClick: () => {
track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_CLICKED}`]: `Early access CTA: Algo fund`,
});
mixpanel.get_distinct_id() &&
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Early access CTA: Algo fund`,
});
toggleModal("hubspot-fund");
},
}}
@ -509,9 +516,10 @@ const Homepage = () => {
cta={{
label: "I want early access!",
onClick: () => {
track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_CLICKED}`]: `Early access CTA: developer`,
});
mixpanel.get_distinct_id() &&
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Early access CTA: developer`,
});
toggleModal("hubspot-developer");
},
}}
@ -520,9 +528,10 @@ const Homepage = () => {
network: "github",
label: "See our github",
onClick: () => {
track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_CLICKED}`]: `Github link in landing page`,
});
mixpanel.get_distinct_id() &&
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Github link in landing page`,
});
},
}}
elementName={"element3"}
@ -568,9 +577,10 @@ const Homepage = () => {
colorScheme="suggested"
id="test"
onClick={() => {
track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_CLICKED}`]: `Join our discord`,
});
mixpanel.get_distinct_id() &&
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Join our discord`,
});
toggleModal("hubspot");
}}
>

Wyświetl plik

@ -13,13 +13,13 @@ const AppContext = (props) => {
return (
<UserProvider>
<ModalProvider>
<AnalyticsProvider>
<StripeProvider>
<ChakraProvider theme={theme}>
<UIProvider>{props.children}</UIProvider>
</ChakraProvider>
</StripeProvider>
</AnalyticsProvider>
<StripeProvider>
<ChakraProvider theme={theme}>
<UIProvider>
<AnalyticsProvider>{props.children}</AnalyticsProvider>
</UIProvider>
</ChakraProvider>
</StripeProvider>
</ModalProvider>
</UserProvider>
);

Wyświetl plik

@ -1,13 +1,13 @@
import { Flex, Box } from "@chakra-ui/react";
import React, { useEffect, useRef, useState } from "react";
import { useRouter, useAnalytics } from "../core/hooks";
import { useRouter } from "../core/hooks";
import mixpanel from "mixpanel-browser";
const Scrollable = (props) => {
const scrollerRef = useRef();
const router = useRouter();
const [path, setPath] = useState();
const [scrollDepth, setScrollDepth] = useState(0);
const { mixpanel, isLoaded } = useAnalytics();
const getScrollPrecent = ({ currentTarget }) => {
const scroll_level =
@ -20,7 +20,7 @@ const Scrollable = (props) => {
const currentScroll = Math.ceil(getScrollPrecent(e) / 10);
if (currentScroll > scrollDepth) {
setScrollDepth(currentScroll);
isLoaded &&
mixpanel.get_distinct_id() &&
mixpanel.people.increment({
[`Scroll depth at: ${router.nextRouter.pathname}`]: currentScroll,
});

Wyświetl plik

@ -1,5 +1,4 @@
export { queryCacheProps as hookCommon } from "./hookCommon";
export { default as useAnalytics } from "./useAnalytics";
export { default as useAuthResultHandler } from "./useAuthResultHandler";
export { default as useChangePassword } from "./useChangePassword";
export { default as useClientID } from "./useClientID";

Wyświetl plik

@ -1,38 +0,0 @@
import AnalyticsContext from "../providers/AnalyticsProvider/context";
import { useContext } from "react";
import { useState, useEffect, useCallback } from "react";
const useAnalytics = () => {
const { mixpanel, isLoaded, MIXPANEL_EVENTS, MIXPANEL_PROPS } =
useContext(AnalyticsContext);
const [trackProps, setTrackProps] = useState({
event: null,
props: null,
queued: false,
});
const track = useCallback((e, props) => {
setTrackProps({ event: e, props: props, queued: true });
}, []);
useEffect(() => {
if (isLoaded && trackProps.queued === true) {
mixpanel.track(trackProps.event, trackProps.props);
setTrackProps({ event: null, props: null, queued: false });
}
}, [isLoaded, mixpanel, trackProps]);
const withTracking = (fn, event, props) => {
track(event, props);
return fn;
};
return {
mixpanel,
isLoaded,
track,
MIXPANEL_PROPS,
MIXPANEL_EVENTS,
withTracking,
};
};
export default useAnalytics;

Wyświetl plik

@ -1,7 +1,6 @@
import { useMutation } from "react-query";
import { useToast, useUser, useInviteAccept } from ".";
import { AuthService } from "../services";
import { useAnalytics } from ".";
const LOGIN_TYPES = {
MANUAL: true,
@ -10,7 +9,6 @@ const LOGIN_TYPES = {
const useLogin = (loginType) => {
const { getUser } = useUser();
const toast = useToast();
const analytics = useAnalytics();
const { inviteAccept } = useInviteAccept();
const {
mutate: login,
@ -34,20 +32,6 @@ const useLogin = (loginType) => {
inviteAccept(invite_code);
}
getUser();
if (analytics.isLoaded) {
analytics.mixpanel.people.set_once({
[`${analytics.MIXPANEL_EVENTS.FIRST_LOGIN_DATE}`]:
new Date().toISOString(),
});
analytics.mixpanel.people.set({
[`${analytics.MIXPANEL_EVENTS.LAST_LOGIN_DATE}`]:
new Date().toISOString(),
});
analytics.mixpanel.track(
`${analytics.MIXPANEL_EVENTS.USER_LOGS_IN}`,
{}
);
}
}
},
onError: (error) => {

Wyświetl plik

@ -1,21 +1,14 @@
import { useCallback, useContext } from "react";
import { useMutation, useQueryClient } from "react-query";
import { useUser, useRouter, useAnalytics } from ".";
import { useUser, useRouter } from ".";
import UIContext from "../providers/UIProvider/context";
import { AuthService } from "../services";
const useLogout = () => {
const { setLoggingOut } = useContext(UIContext);
const router = useRouter();
const analytics = useAnalytics();
const { mutate: revoke } = useMutation(AuthService.revoke, {
onSuccess: () => {
if (analytics.isLoaded) {
analytics.mixpanel.track(
`${analytics.MIXPANEL_EVENTS.USER_LOGS_OUT}`,
{}
);
}
localStorage.removeItem("MOONSTREAM_ACCESS_TOKEN");
cache.clear();
setUser(null);

Wyświetl plik

@ -1,16 +1,18 @@
import { useContext } from "react";
import { useMutation } from "react-query";
import { AuthService } from "../services";
import { useUser, useToast, useInviteAccept, useRouter, useAnalytics } from ".";
import { useUser, useToast, useInviteAccept, useRouter } from ".";
import UIContext from "../providers/UIProvider/context";
import mixpanel from "mixpanel-browser";
import { MIXPANEL_EVENTS } from "../providers/AnalyticsProvider/constants";
const useSignUp = (source) => {
const ui = useContext(UIContext);
const router = useRouter();
const { getUser } = useUser();
const toast = useToast();
const { inviteAccept } = useInviteAccept();
const analytics = useAnalytics();
const {
mutate: signUp,
@ -26,11 +28,11 @@ const useSignUp = (source) => {
inviteAccept(invite_code);
}
if (analytics.isLoaded) {
analytics.mixpanel.track(
`${analytics.MIXPANEL_EVENTS.CONVERT_TO_USER}`,
{ full_url: router.nextRouter.asPath, code: source }
);
if (mixpanel.get_distinct_id()) {
mixpanel.track(`${MIXPANEL_EVENTS.CONVERT_TO_USER}`, {
full_url: router.nextRouter.asPath,
code: source,
});
}
getUser();
ui.setisOnboardingComplete(false);

Wyświetl plik

@ -1,21 +1,18 @@
import { useToast as useChakraToast, Box } from "@chakra-ui/react";
import React, { useCallback } from "react";
import { useAnalytics } from ".";
import mixpanel from "mixpanel-browser";
import { MIXPANEL_EVENTS } from "../providers/AnalyticsProvider/constants";
const useToast = () => {
const chakraToast = useChakraToast();
const analytics = useAnalytics();
const toast = useCallback(
(message, type) => {
if (analytics.isLoaded && type === "error") {
analytics.mixpanel.track(
`${analytics.MIXPANEL_EVENTS.TOAST_ERROR_DISPLAYED}`,
{
status: message?.response?.status,
detail: message?.response?.data.detail,
}
);
if (mixpanel.get_distinct_id() && type === "error") {
mixpanel.track(`${MIXPANEL_EVENTS.TOAST_ERROR_DISPLAYED}`, {
status: message?.response?.status,
detail: message?.response?.data.detail,
});
}
const background = type === "error" ? "unsafe.500" : "suggested.500";
const userMessage =
@ -43,7 +40,7 @@ const useToast = () => {
),
});
},
[chakraToast, analytics]
[chakraToast]
);
return toast;

Wyświetl plik

@ -8,6 +8,12 @@ export const MIXPANEL_PROPS = {
USER_SPECIALITY: "user speciality",
};
export const MIXPANEL_GROUPS = {
DEVELOPERS: "developers",
TRADERS: "traders",
FUND: "funds",
};
export const MIXPANEL_EVENTS = {
FIRST_LOGIN_DATE: "First login date",
LAST_LOGIN_DATE: "Last login date",
@ -20,7 +26,14 @@ export const MIXPANEL_EVENTS = {
PAGEVIEW: "Page view",
PRICING_PLAN_CLICKED: "Pricing Plan clicked",
BUTTON_CLICKED: "Button clicked",
LEFT_PAGE: "Left page",
BEACON: "beacon",
ONBOARDING_COMPLETED: "Onbording complete",
SESSIONS_COUNT: "Sessions Counter",
ONBOARDING_STEP: "Onboarding step",
ONBOARDING_STATE: "Onboarding state",
TIMES_VISITED: "Page visit times",
FORM_SUBMITTED: "form submitted",
PAGEVIEW_DURATION: "Time spent on page",
};
export default MIXPANEL_EVENTS;

Wyświetl plik

@ -1,66 +1,161 @@
import React, { useEffect, useState } from "react";
import React, { useContext, useEffect, useState } from "react";
import mixpanel from "mixpanel-browser";
import AnalyticsContext from "./context";
import { useClientID, useUser, useRouter } from "../../hooks";
import { MIXPANEL_EVENTS, MIXPANEL_PROPS } from "./constants";
import UIContext from "../UIProvider/context";
const AnalyticsProvider = ({ children }) => {
const clientID = useClientID();
const analytics = process.env.NEXT_PUBLIC_MIXPANEL_TOKEN;
const { user } = useUser();
const [isLoaded, setIsLoaded] = useState(false);
const { user, isInit } = useUser();
const [isMixpanelReady, setIsLoaded] = useState(false);
const router = useRouter();
const ui = useContext(UIContext);
// ********** OBOARDING STATE **************
useEffect(() => {
if (ui.onboardingState && isMixpanelReady) {
mixpanel.people.set(MIXPANEL_EVENTS.ONBOARDING_STATE, {
state: { ...ui.onboardingState },
});
}
}, [ui.onboardingState, isMixpanelReady]);
useEffect(() => {
if (ui.isOnboardingComplete && isMixpanelReady && user) {
mixpanel.people.set(MIXPANEL_EVENTS.ONBOARDING_COMPLETED, true);
}
}, [ui.isOnboardingComplete, isMixpanelReady, user]);
// ********** ONBOARDING STEP and TIMING **************
const [previousOnboardingStep, setPreviousOnboardingStep] = useState(false);
useEffect(() => {
if (isMixpanelReady && router.nextRouter.pathname === "/welcome") {
if (!previousOnboardingStep) {
mixpanel.time_event(MIXPANEL_EVENTS.ONBOARDING_STEP);
setPreviousOnboardingStep(ui.onboardingStep);
}
if (
previousOnboardingStep &&
previousOnboardingStep !== ui.onboardingStep
) {
mixpanel.track(MIXPANEL_EVENTS.ONBOARDING_STEP, {
step: previousOnboardingStep,
isBeforeUnload: false,
});
setPreviousOnboardingStep(false);
}
} else if (previousOnboardingStep) {
mixpanel.track(MIXPANEL_EVENTS.ONBOARDING_STEP, {
step: previousOnboardingStep,
isBeforeUnload: false,
});
setPreviousOnboardingStep(false);
}
}, [
previousOnboardingStep,
ui.onboardingStep,
isMixpanelReady,
router.nextRouter.pathname,
]);
// ********** PING_PONG **************
useEffect(() => {
let durationSeconds = 0;
const intervalId =
isLoaded &&
isMixpanelReady &&
setInterval(() => {
durationSeconds = durationSeconds + 1;
durationSeconds = durationSeconds + 30;
mixpanel.track(
MIXPANEL_EVENTS.LEFT_PAGE,
MIXPANEL_EVENTS.BEACON,
{
duration_seconds: durationSeconds,
url: router.nextRouter.pathname,
query: router.query,
pathParams: router.params,
},
{ transport: "sendBeacon" }
);
}, 30000);
return () => clearInterval(intervalId);
// eslint-disable-next-line
}, [isLoaded]);
}, [isMixpanelReady, router.nextRouter.pathname]);
// ********** TIME SPENT ON PATH**************
const [previousPathname, setPreviousPathname] = useState(false);
useEffect(() => {
isLoaded &&
if (isMixpanelReady) {
if (!previousPathname) {
mixpanel.time_event(MIXPANEL_EVENTS.PAGEVIEW_DURATION);
setPreviousPathname(router.nextRouter.pathname);
}
if (previousPathname && previousPathname !== router.nextRouter.pathname) {
mixpanel.track(MIXPANEL_EVENTS.PAGEVIEW_DURATION, {
url: previousPathname,
isBeforeUnload: false,
});
setPreviousPathname(false);
}
}
}, [router.nextRouter.pathname, previousPathname, isMixpanelReady]);
// ********** PAGES VIEW **************
useEffect(() => {
if (isMixpanelReady && ui.sessionId && router.nextRouter.pathname) {
mixpanel.track(MIXPANEL_EVENTS.PAGEVIEW, {
url: router.nextRouter.pathname,
query: router.query,
pathParams: router.params,
sessionID: ui.sessionId,
});
}, [router.nextRouter.pathname, router.query, router.params, isLoaded]);
useEffect(() => {
try {
mixpanel.init(analytics, {
api_host: "https://api.mixpanel.com",
loaded: () => {
setIsLoaded(true);
mixpanel.identify(clientID);
},
mixpanel.people.increment([
`${MIXPANEL_EVENTS.TIMES_VISITED} ${router.nextRouter.pathname}`,
]);
}
const urlForUnmount = router.nextRouter.pathname;
const closeListener = () => {
mixpanel.track(MIXPANEL_EVENTS.PAGEVIEW_DURATION, {
url: urlForUnmount,
isBeforeUnload: true,
});
} catch (error) {
console.warn("loading mixpanel failed:", error);
};
window.addEventListener("beforeunload", closeListener);
//cleanup function fires on useEffect unmount
//https://reactjs.org/docs/hooks-effect.html
return () => {
window.removeEventListener("beforeunload", closeListener);
};
}, [router.nextRouter.pathname, isMixpanelReady, ui.sessionId]);
// ********** SESSION STATE **************
useEffect(() => {
if (clientID) {
try {
mixpanel.init(analytics, {
api_host: "https://api.mixpanel.com",
loaded: () => {
setIsLoaded(true);
mixpanel.identify(clientID);
},
});
} catch (error) {
console.warn("loading mixpanel failed:", error);
}
}
}, [analytics, clientID]);
useEffect(() => {
isMixpanelReady && mixpanel.register("sessionId", ui.sessionId);
}, [ui.sessionId, isMixpanelReady]);
// ********** USER STATE **************
useEffect(() => {
if (user) {
try {
if (isLoaded) {
if (isMixpanelReady) {
mixpanel.people.set({
[`${MIXPANEL_EVENTS.LAST_VISITED}`]: new Date().toISOString(),
});
@ -74,11 +169,36 @@ const AnalyticsProvider = ({ children }) => {
console.error("could not set up people in mixpanel:", err);
}
}
}, [user, isLoaded, clientID]);
}, [user, isMixpanelReady, clientID]);
useEffect(() => {
if (isMixpanelReady && user) {
mixpanel.people.set_once({
[`${MIXPANEL_EVENTS.FIRST_LOGIN_DATE}`]: new Date().toISOString(),
});
mixpanel.people.set({
[`${MIXPANEL_EVENTS.LAST_LOGIN_DATE}`]: new Date().toISOString(),
});
mixpanel.track(`${MIXPANEL_EVENTS.USER_LOGS_IN}`, {});
}
}, [user, isMixpanelReady]);
useEffect(() => {
if (isMixpanelReady && ui.isLoggingOut) {
mixpanel.track(`${MIXPANEL_EVENTS.USER_LOGS_OUT}`, {});
}
}, [ui.isLoggingOut, isMixpanelReady]);
// ********** USER BOUNCE TIME **************
useEffect(() => {
if (!user && isInit && isMixpanelReady) {
mixpanel.time_event(MIXPANEL_EVENTS.CONVERT_TO_USER);
}
}, [user, isInit, isMixpanelReady]);
return (
<AnalyticsContext.Provider
value={{ mixpanel, isLoaded, MIXPANEL_EVENTS, MIXPANEL_PROPS }}
value={{ mixpanel, isMixpanelReady, MIXPANEL_EVENTS, MIXPANEL_PROPS }}
>
{children}
</AnalyticsContext.Provider>

Wyświetl plik

@ -181,10 +181,19 @@ const UIProvider = ({ children }) => {
);
const [onboardingStep, setOnboardingStep] = useState(() => {
const step = onboardingSteps.findIndex(
(event) => onboardingState[event.step] === 0
//First find onboarding step that was viewed once (resume where left)
// If none - find step that was never viewed
// if none - set onboarding to zero
let step = onboardingSteps.findIndex(
(event) => onboardingState[event.step] === 1
);
if (step === -1 && isOnboardingComplete) return onboardingSteps.length - 1;
step =
step === -1
? onboardingSteps.findIndex(
(event) => onboardingState[event.step] === 0
)
: step;
if (step === -1 && isOnboardingComplete) return 0;
else if (step === -1 && !isOnboardingComplete) return 0;
else return step;
});
@ -263,6 +272,8 @@ const UIProvider = ({ children }) => {
setisOnboardingComplete,
onboardingSteps,
setOnboardingState,
onboardingState,
isLoggingOut,
}}
>
{children}