diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 65a71f3..41fd3a5 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -20,6 +20,8 @@ jobs: REACT_APP_BEE_HOST: https://api.test-node.staging.ethswarm.org/ REACT_APP_BEE_DEBUG_HOST: https://debug.test-node.staging.ethswarm.org/ REACT_APP_DEV_MODE: 1 + REACT_APP_SENTRY_KEY: ${{ secrets.SENTRY_KEY }} + REACT_APP_SENTRY_ENVIRONMENT: 'preview' steps: - uses: actions/checkout@v2 diff --git a/src/components/Feedback.tsx b/src/components/Feedback.tsx new file mode 100644 index 0000000..558152a --- /dev/null +++ b/src/components/Feedback.tsx @@ -0,0 +1,95 @@ +import { ReactElement, useEffect, useState } from 'react' +import * as Sentry from '@sentry/react' +import { Link } from '@material-ui/core' +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' +import { MessageSquare } from 'react-feather' + +import config from '../config' +import SideBarItem from './SideBarItem' + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + link: { + color: '#9f9f9f', + textDecoration: 'none', + '&:hover': { + textDecoration: 'none', + + // https://github.com/mui-org/material-ui/issues/22543 + '@media (hover: none)': { + textDecoration: 'none', + }, + }, + }, + + icon: { + height: theme.spacing(4), + }, + }), +) + +/** + * Parses Sentry DNS so it could be transformed into API call + * Sentry DNS like https://1asfasdf2312asdf3@o132123.ingest.sentry.io/13123123 + */ +const SENTRY_PARSING_REGEX = /^https:\/\/(?\w+)@(?\w+)\.ingest\.sentry\.io\/(?\d+)$/gm + +async function isSentryReachable(): Promise { + const key = config.SENTRY_KEY + + if (!key) { + return false + } + + const match = SENTRY_PARSING_REGEX.exec(key) + + if (!match) { + return false + } + + const url = `https://${match.groups?.sub}.ingest.sentry.io/api/${match.groups?.path}/envelope/?sentry_key=${match.groups?.key}` + + try { + await fetch(url, { method: 'POST' }) + + // Since we got some reply (even though most probably with some error) that means Sentry is reachable ==> lets provide the Feedback form + return true + } catch (e) { + // If an error was thrown than the request was blocked by the browser so Sentry is not accessible to us + return false + } +} + +function showFeedbackForm(): void { + const eventId = Sentry.captureMessage('User feedback') + Sentry.showReportDialog({ + eventId, + title: 'Provide us feedback!', + subtitle: 'Share with us what you like and/or dislike.', + subtitle2: 'We will be very happy.', + labelComments: 'What is your impression about this app?', + labelSubmit: 'Send Feedback', + }) +} + +export default function Feedback(): ReactElement { + const [sentryEnabled, setSentryEnabled] = useState(false) + const classes = useStyles() + + // Run this only on component mount to verify once that Sentry is reachable + useEffect(() => { + isSentryReachable().then(result => { + setSentryEnabled(result) + }) + }, []) + + if (sentryEnabled) { + return ( + + } label={Send feedback} /> + + ) + } + + return <> +} diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 9dd1ce9..8268c5e 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -11,6 +11,7 @@ import { Context } from '../providers/Bee' import { ROUTES } from '../routes' import SideBarItem from './SideBarItem' import SideBarStatus from './SideBarStatus' +import Feedback from './Feedback' const navBarItems = [ { @@ -60,6 +61,7 @@ const useStyles = makeStyles((theme: Theme) => drawerPaper: { width: drawerWidth, backgroundColor: '#212121', + zIndex: 988, }, logo: { marginLeft: theme.spacing(8), @@ -131,9 +133,12 @@ export default function SideBar(): ReactElement { - - - + + + + + + diff --git a/src/config.ts b/src/config.ts index d3bc1b0..7fa1bfd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,10 +7,12 @@ class Config { public readonly GITHUB_REPO_URL: string public readonly BEE_DESKTOP_URL: string public readonly SENTRY_KEY: string | undefined + public readonly SENTRY_ENVIRONMENT: string | undefined constructor() { this.BEE_API_HOST = sessionStorage.getItem('api_host') ?? process.env.REACT_APP_BEE_HOST ?? 'http://localhost:1633' this.SENTRY_KEY = process.env.REACT_APP_SENTRY_KEY + this.SENTRY_ENVIRONMENT = process.env.REACT_APP_SENTRY_ENVIRONMENT this.BEE_DEBUG_API_HOST = sessionStorage.getItem('debug_api_host') ?? process.env.REACT_APP_BEE_DEBUG_HOST ?? 'http://localhost:1635' this.BLOCKCHAIN_EXPLORER_URL = diff --git a/src/index.css b/src/index.css index a91ac00..6474a6b 100644 --- a/src/index.css +++ b/src/index.css @@ -17,3 +17,10 @@ body { code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } + +.sentry-error-embed * { + font-family: 'Work Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif !important; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/src/utils/sentry.ts b/src/utils/sentry.ts index 94fa155..e9232cf 100644 --- a/src/utils/sentry.ts +++ b/src/utils/sentry.ts @@ -21,6 +21,7 @@ export async function initSentry(): Promise { Sentry.init({ dsn: config.SENTRY_KEY, release: packageJson.version, + environment: config.SENTRY_ENVIRONMENT, tunnel: tunnelAvailable ? `${config.BEE_DESKTOP_URL}/sentry` : undefined, integrations: [new BrowserTracing({ tracingOrigins: ['localhost'] })], tracesSampleRate: 0.3,