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

Google analytics integration in the new webgui #642

Merged
Merged
Show file tree
Hide file tree
Changes from 13 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
6 changes: 6 additions & 0 deletions webgui-new/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webgui-new/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"react": "^18.2.0",
"react-diff-viewer-continued": "^3.2.6",
"react-dom": "^18.2.0",
"react-ga4": "^2.1.0",
"react-i18next": "^13.0.1",
"react-icons": "^4.8.0",
"react-toastify": "^9.1.2",
Expand Down
9 changes: 8 additions & 1 deletion webgui-new/src/components/codebites/codebites-node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { IconButton, Tooltip } from '@mui/material';
import { Close } from '@mui/icons-material';
import { AppContext } from 'global-context/app-context';
import dagre from 'dagre';
import { sendGAEvent } from 'utils/analytics';

type CodeBitesElement = {
astNodeInfo: AstNodeInfo;
Expand Down Expand Up @@ -94,6 +95,7 @@ class CustomOffsetGutterMarker extends GutterMarker {

export const CodeBitesNode = ({ data }: NodeProps<DataProps>): JSX.Element => {
const { diagramGenId: initialNodeId } = useContext(AppContext);
const appCtx = useContext(AppContext);
const { theme } = useContext(ThemeContext);

const [fileInfo, setFileInfo] = useState<FileInfo | undefined>(undefined);
Expand All @@ -108,11 +110,16 @@ export const CodeBitesNode = ({ data }: NodeProps<DataProps>): JSX.Element => {
const init = async () => {
const initFileInfo = await getFileInfo(data.astNodeInfo.range?.file as string);
const initText = await getCppSourceText(data.astNodeInfo.id as string);
sendGAEvent({
event_action: 'code_bites',
event_category: appCtx.workspaceId,
event_label: `${initFileInfo?.name}: ${initText}`,
});
setFileInfo(initFileInfo);
setText(initText);
};
init();
}, [data.astNodeInfo]);
}, [appCtx.workspaceId, data.astNodeInfo]);

const handleClick = async () => {
if (!editorRef.current) return;
Expand Down
11 changes: 11 additions & 0 deletions webgui-new/src/components/codemirror-editor/codemirror-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { RouterQueryType } from 'utils/types';
import { Tooltip, alpha } from '@mui/material';
import * as SC from './styled-components';
import { useTranslation } from 'react-i18next';
import { sendGAEvent } from 'utils/analytics';

export const CodeMirrorEditor = (): JSX.Element => {
const { t } = useTranslation();
Expand Down Expand Up @@ -111,6 +112,11 @@ export const CodeMirrorEditor = (): JSX.Element => {
? null
: await getCppAstNodeInfoByPosition(fileInfo?.id as string, line.number, column);
if (astNodeInfo) {
sendGAEvent({
event_action: 'click_on_word',
event_category: appCtx.workspaceId,
event_label: `${fileInfo?.name}: ${astNodeInfo.astNodeValue}`,
});
dispatchSelection(astNodeInfo?.range?.range as Range);
router.push({
pathname: '/project',
Expand All @@ -132,6 +138,11 @@ export const CodeMirrorEditor = (): JSX.Element => {
column: line.length + 1,
}),
});
sendGAEvent({
event_action: 'click_on_word',
event_category: appCtx.workspaceId,
event_label: `${fileInfo?.name}: ${convertSelectionRangeToString(range)}`,
});
dispatchSelection(range);
router.push({
pathname: '/project',
Expand Down
186 changes: 186 additions & 0 deletions webgui-new/src/components/cookie-notice/cookie-notice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getStore, setStore } from 'utils/store';
import ReactGA from 'react-ga4';
import {
Paper,
Typography,
Button,
IconButton,
Snackbar,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
TableContainer,
TableRow,
TableCell,
Table,
TableHead,
TableBody,
Link,
} from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { useRouter } from 'next/router';

export const CookieNotice = (): JSX.Element => {
const { t } = useTranslation();
const router = useRouter();
const [isCookieConsent, setIsCookieConsent] = useState<boolean | undefined>(undefined);
const [gaTrackingCode, setGaTrackingCode] = useState<string | undefined>(undefined);
const [openPolicyModal, setOpenPolicyModal] = useState(false);
const [showNoticeSnackbar, setShowNoticeSnackbar] = useState(true);

useEffect(() => {
const fetchGaTrackingCode = async () => {
try {
const res = await fetch(`/ga.txt`);
if (res.status !== 200) {
setGaTrackingCode(undefined);
return;
}
const gaCode = await res.text();
setGaTrackingCode(gaCode);
} catch (e) {
// network-related error
setGaTrackingCode(undefined);
}
const store = getStore();
setIsCookieConsent(store.storedCookieConsent);
};
fetchGaTrackingCode();
}, []);

useEffect(() => {
if (!isCookieConsent || !gaTrackingCode) {
ReactGA.reset();
return;
}
if (!ReactGA.isInitialized) {
ReactGA.initialize(gaTrackingCode);
console.log(`Google Analytics initialized - ${gaTrackingCode}`);
}

const handleRouteChange = (url: string) => {
ReactGA.send({ hitType: 'pageview', page: url, title: window.document.title });
};
router.events.on('routeChangeComplete', handleRouteChange);

return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [isCookieConsent, gaTrackingCode, router.events]);

const handleCookieAccept = () => {
setIsCookieConsent(true);
setShowNoticeSnackbar(false);
setStore({ storedCookieConsent: true });
};

if (!isCookieConsent && gaTrackingCode)
return (
<>
<Snackbar
open={showNoticeSnackbar}
autoHideDuration={null}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
>
<Paper
sx={{
p: 2,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<Typography variant="body2">
{t('cookie.INTRO_TEXT')}
<Button color="primary" size="small" onClick={() => setOpenPolicyModal(true)}>
{t('cookie.LEARN_MORE')}
</Button>
</Typography>
<Button
color="primary"
size="small"
variant="contained"
onClick={() => {
handleCookieAccept();
}}
>
{t('cookie.ACCEPT')}
</Button>
<IconButton
size="small"
color="inherit"
sx={{ ml: 2 }}
onClick={() => setShowNoticeSnackbar(false)}
>
<CloseIcon fontSize="small" />
</IconButton>
</Paper>
</Snackbar>
<Dialog open={openPolicyModal} maxWidth="lg" onClose={() => setOpenPolicyModal(false)}>
<DialogTitle>{t('cookiePolicy.TITLE')}</DialogTitle>
<DialogContent>
<>
<Typography sx={{ marginBottom: '0.8rem' }} variant="body1">
{t('cookiePolicy.sections.cookies.WHAT_COOKIES')}
</Typography>
<Typography sx={{ marginBottom: '0.8rem' }} variant="body2">
{t('cookiePolicy.sections.cookies.WHAT_COOKIES_DESCRIPTION')}
</Typography>
<Typography sx={{ marginBottom: '0.8rem' }} variant="body1">
{t('cookiePolicy.sections.cookies.HOW_USE_TITLE')}
</Typography>
<TableContainer component={Paper} sx={{ marginBottom: '0.8rem' }}>
<Table sx={{ minWidth: 650 }} aria-label="cookie-table">
<TableHead>
<TableRow>
<TableCell>{t('cookiePolicy.sections.cookies.SERVICE')}</TableCell>
<TableCell>{t('cookiePolicy.sections.cookies.COOKIE_NAMES')}</TableCell>
<TableCell>{t('cookiePolicy.sections.cookies.DURATION')}</TableCell>
<TableCell>{t('cookiePolicy.sections.cookies.PURPOSE')}</TableCell>
<TableCell>{t('cookiePolicy.sections.cookies.MORE_INFORMATION')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow key={1} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell component="th" scope="row">
<b>{t('cookiePolicy.sections.cookies.googleAnalytics.SERVICE')}</b>
</TableCell>
<TableCell>
<b>{t('cookiePolicy.sections.cookies.googleAnalytics.COOKIE_NAMES')}</b>
</TableCell>
<TableCell>
<b>{t('cookiePolicy.sections.cookies.googleAnalytics.DURATION')}</b>
</TableCell>
<TableCell>
<b>{t('cookiePolicy.sections.cookies.googleAnalytics.PURPOSE')}</b>
</TableCell>
<TableCell>
<Link
target="_blank"
rel="noreferrer"
href={t('cookiePolicy.sections.cookies.googleAnalytics.MORE_INFORMATION')}
>
<b>{t('cookiePolicy.sections.cookies.MORE_INFORMATION')}</b>
</Link>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
<Typography variant="body2">
{t('cookiePolicy.sections.cookies.CONCLUSION')}
</Typography>
</>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpenPolicyModal(false)} color="primary"></Button>
</DialogActions>
</Dialog>
</>
);

return <></>;
};
16 changes: 14 additions & 2 deletions webgui-new/src/components/diagrams/diagrams.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { convertSelectionRangeToString } from 'utils/utils';
import { useRouter } from 'next/router';
import { RouterQueryType } from 'utils/types';
import { useTranslation } from 'react-i18next';
import { sendGAEvent } from 'utils/analytics';

export const Diagrams = (): JSX.Element => {
const { t } = useTranslation();
Expand Down Expand Up @@ -67,7 +68,18 @@ export const Diagrams = (): JSX.Element => {
: initDiagramInfo instanceof AstNodeInfo
? await getCppDiagram(appCtx.diagramGenId, parseInt(appCtx.diagramTypeId))
: '';


sendGAEvent({
event_action: `load_diagram: ${appCtx.diagramTypeId}`,
event_category: appCtx.workspaceId,
event_label:
initDiagramInfo instanceof FileInfo
? initDiagramInfo.name
: initDiagramInfo instanceof AstNodeInfo
? initDiagramInfo.astNodeValue
: '',
});

const parser = new DOMParser();
const parsedDiagram = parser.parseFromString(diagram, 'text/xml');
const diagramSvg = parsedDiagram.getElementsByTagName('svg')[0];
Expand All @@ -81,7 +93,7 @@ export const Diagrams = (): JSX.Element => {
setDiagramInfo(initDiagramInfo);
};
init();
}, [appCtx.diagramGenId, appCtx.diagramTypeId, appCtx.diagramType]);
}, [appCtx.diagramGenId, appCtx.diagramTypeId, appCtx.diagramType, appCtx.workspaceId]);

const generateDiagram = async (e: MouseEvent) => {
const parentNode = (e.target as HTMLElement)?.parentElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useRouter } from 'next/router';
import { RouterQueryType } from 'utils/types';
import { useTranslation } from 'react-i18next';
import { diagramTypeArray } from 'enums/entity-types';
import { sendGAEvent } from 'utils/analytics';

export const EditorContextMenu = ({
contextMenu,
Expand Down Expand Up @@ -69,8 +70,14 @@ export const EditorContextMenu = ({

const getDocs = async () => {
const initDocs = await getCppDocumentation(astNodeInfo?.id as string);
const fileInfo = await getFileInfo(appCtx.projectFileId as string);
const parser = new DOMParser();
const parsedHTML = parser.parseFromString(initDocs, 'text/html');
sendGAEvent({
event_action: 'documentation',
event_category: appCtx.workspaceId,
event_label: `${fileInfo?.name}: ${astNodeInfo?.astNodeValue}`,
});
setModalOpen(true);
setContextMenu(null);
if (!docsContainerRef.current) return;
Expand All @@ -87,6 +94,12 @@ export const EditorContextMenu = ({
const initAstHTML = await getAsHTMLForNode(astNodeInfo?.id as string);
const parser = new DOMParser();
const parsedHTML = parser.parseFromString(initAstHTML, 'text/html');
const fileInfo = await getFileInfo(appCtx.projectFileId as string);
sendGAEvent({
event_action: 'cpp_reparse_node',
event_category: appCtx.workspaceId,
event_label: `${fileInfo?.name}: ${astNodeInfo?.astNodeValue}`,
});
setModalOpen(true);
setContextMenu(null);
if (!astHTMLContainerRef.current) return;
Expand Down Expand Up @@ -115,6 +128,12 @@ export const EditorContextMenu = ({
}

const fileId = def.range?.file as string;
const fileInfo = await getFileInfo(appCtx.projectFileId as string);
sendGAEvent({
event_action: 'jump_to_def',
event_category: appCtx.workspaceId,
event_label: `${fileInfo?.name}: ${astNodeInfo.astNodeValue}`,
});

router.push({
pathname: '/project',
Expand Down Expand Up @@ -145,6 +164,11 @@ export const EditorContextMenu = ({
currentRepo?.repoPath as string,
fileInfo?.id as string
);
sendGAEvent({
event_action: 'git_blame',
event_category: appCtx.workspaceId,
event_label: fileInfo?.name,
});
return blameInfo;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useRouter } from 'next/router';
import { RouterQueryType } from 'utils/types';
import { useTranslation } from 'react-i18next';
import { diagramTypeArray } from 'enums/entity-types';
import { sendGAEvent } from 'utils/analytics';

export const FileContextMenu = ({
contextMenu,
Expand Down Expand Up @@ -67,6 +68,11 @@ export const FileContextMenu = ({
{fileInfo && fileInfo.isDirectory && (
<MenuItem
onClick={async () => {
sendGAEvent({
event_action: 'metrics',
event_category: appCtx.workspaceId,
event_label: fileInfo.name,
});
setContextMenu(null);
router.push({
pathname: '/project',
Expand Down
Loading