feat: migrate desktop/mobile error tracking to posthog (#4858)
* feat: migrate desktop and mobile error tracking to PostHog * fix(mobile): remove unsupported tab bar minimize API * Revert "fix(mobile): remove unsupported tab bar minimize API" This reverts commit e234dda51257dc6810f0f78e06ee918adf2214b0.
This commit is contained in:
parent
47a56c5017
commit
0da9f2098e
|
|
@ -1,7 +1,6 @@
|
|||
import { readFileSync } from "node:fs"
|
||||
import { fileURLToPath } from "node:url"
|
||||
|
||||
import { sentryVitePlugin } from "@sentry/vite-plugin"
|
||||
import react from "@vitejs/plugin-react"
|
||||
import { codeInspectorPlugin } from "code-inspector-plugin"
|
||||
import { dirname, resolve } from "pathe"
|
||||
|
|
@ -17,9 +16,6 @@ import i18nCompleteness from "../plugins/vite/utils/i18n-completeness"
|
|||
|
||||
const pkgDir = resolve(dirname(fileURLToPath(import.meta.url)), "..")
|
||||
const pkg = JSON.parse(readFileSync(resolve(pkgDir, "./package.json"), "utf8"))
|
||||
const isCI = process.env.CI === "true" || process.env.CI === "1"
|
||||
const mode = process.argv.find((arg) => arg.startsWith("--mode"))?.split("=")[1]
|
||||
const isStaging = mode === "staging"
|
||||
|
||||
const getChangelogFileContent = () => {
|
||||
const { version: pkgVersion } = pkg
|
||||
|
|
@ -76,36 +72,6 @@ export const viteRenderBaseConfig = {
|
|||
}),
|
||||
circularImportRefreshPlugin(),
|
||||
|
||||
sentryVitePlugin({
|
||||
org: "follow-rg",
|
||||
project: "follow",
|
||||
disable: !isCI,
|
||||
bundleSizeOptimizations: {
|
||||
excludeDebugStatements: true,
|
||||
|
||||
// Only relevant if you added `replayIntegration`
|
||||
excludeReplayIframe: true,
|
||||
excludeReplayShadowDom: true,
|
||||
excludeReplayWorker: true,
|
||||
},
|
||||
moduleMetadata: {
|
||||
appVersion: process.env.NODE_ENV === "development" ? "dev" : pkg.version,
|
||||
electron: false,
|
||||
},
|
||||
sourcemaps: {
|
||||
filesToDeleteAfterUpload: isStaging
|
||||
? []
|
||||
: [
|
||||
"out/web/assets/*.js.map",
|
||||
"out/web/vendor/*.js.map",
|
||||
"out/rn-web/assets/*.js.map",
|
||||
"out/rn-web/vendor/*.js.map",
|
||||
"dist/renderer/assets/*.js.map",
|
||||
"dist/renderer/vendor/*.css.map",
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
astPlugin,
|
||||
customI18nHmrPlugin(),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
"@follow/shared": "workspace:*",
|
||||
"@follow/utils": "workspace:*",
|
||||
"@openpanel/web": "1.0.7",
|
||||
"@sentry/electron": "7.2.0",
|
||||
"builder-util-runtime": "9.5.1",
|
||||
"electron-context-menu": "4.1.1",
|
||||
"electron-ipc-decorator": "0.2.0",
|
||||
|
|
|
|||
|
|
@ -1,14 +1,8 @@
|
|||
import { app, protocol } from "electron"
|
||||
import path from "pathe"
|
||||
|
||||
import { initializeSentry } from "./sentry"
|
||||
|
||||
if (import.meta.env.DEV) app.setPath("userData", path.join(app.getPath("appData"), "Folo(dev)"))
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
scheme: "sentry-ipc",
|
||||
privileges: { bypassCSP: true, corsEnabled: true, supportFetchAPI: true, secure: true },
|
||||
},
|
||||
{
|
||||
scheme: "app",
|
||||
privileges: {
|
||||
|
|
@ -19,5 +13,3 @@ protocol.registerSchemesAsPrivileged([
|
|||
},
|
||||
},
|
||||
])
|
||||
// Solve Sentry SDK should be initialized before the Electron app 'ready' event is fired
|
||||
initializeSentry()
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
import { captureConsoleIntegration, init, setTag } from "@sentry/electron/main"
|
||||
import { app } from "electron"
|
||||
import { FetchError } from "ofetch"
|
||||
|
||||
import { DEVICE_ID } from "./constants/system"
|
||||
|
||||
export const initializeSentry = () => {
|
||||
init({
|
||||
dsn: process.env.VITE_SENTRY_DSN,
|
||||
integrations: [
|
||||
captureConsoleIntegration({
|
||||
levels: ["error"],
|
||||
}),
|
||||
],
|
||||
|
||||
beforeSend(event, hint) {
|
||||
const error = hint.originalException
|
||||
|
||||
if (
|
||||
error instanceof Error &&
|
||||
(/Network Error/i.test(error.message) ||
|
||||
/Fetch Error/i.test(error.message) ||
|
||||
/XHR Error/i.test(error.message) ||
|
||||
/adsbygoogle/i.test(error.message) ||
|
||||
/Failed to fetch/i.test(error.message) ||
|
||||
error.message.includes("fetch failed"))
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (error instanceof FetchError) {
|
||||
return null
|
||||
}
|
||||
|
||||
return event
|
||||
},
|
||||
})
|
||||
setTag("device_id", DEVICE_ID)
|
||||
setTag("app_version", app.getVersion())
|
||||
setTag("build", "electron")
|
||||
}
|
||||
|
|
@ -38,7 +38,6 @@
|
|||
"@radix-ui/react-popover": "1.1.15",
|
||||
"@radix-ui/react-slider": "1.3.6",
|
||||
"@radix-ui/react-slot": "1.2.4",
|
||||
"@sentry/react": "10.38.0",
|
||||
"@shikijs/transformers": "3.22.0",
|
||||
"@splinetool/react-spline": "4.1.0",
|
||||
"@tanstack/query-sync-storage-persister": "5.90.22",
|
||||
|
|
@ -88,6 +87,7 @@
|
|||
"react": "19.0.0",
|
||||
"react-blurhash": "0.3.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-error-boundary": "6.1.0",
|
||||
"react-fast-compare": "3.2.2",
|
||||
"react-fast-marquee": "1.6.5",
|
||||
"react-google-recaptcha-v3": "1.11.0",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import type { FallbackRender } from "@sentry/react"
|
||||
import { ErrorBoundary } from "@sentry/react"
|
||||
import type { FC, PropsWithChildren } from "react"
|
||||
import { createElement, Suspense, useCallback } from "react"
|
||||
|
||||
import { getErrorFallback } from "../errors"
|
||||
import type { ErrorComponentType } from "../errors/enum"
|
||||
import PageErrorFallback from "../errors/PageError"
|
||||
import type { FallbackRender } from "./ErrorBoundary"
|
||||
import { ErrorBoundary } from "./ErrorBoundary"
|
||||
|
||||
export interface AppErrorBoundaryProps extends PropsWithChildren {
|
||||
height?: number | string
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
import { tracker } from "@follow/tracker"
|
||||
import type { PropsWithChildren, ReactNode } from "react"
|
||||
import type { FallbackProps } from "react-error-boundary"
|
||||
import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary"
|
||||
|
||||
export type ErrorFallbackProps = Omit<FallbackProps, "resetErrorBoundary"> &
|
||||
FallbackProps & {
|
||||
resetError: () => void
|
||||
}
|
||||
export type FallbackRender = (props: ErrorFallbackProps) => ReactNode
|
||||
|
||||
interface ErrorBoundaryProps extends PropsWithChildren {
|
||||
fallback?: FallbackRender
|
||||
fallbackRender?: FallbackRender
|
||||
handled?: boolean
|
||||
beforeCapture?: (scope: unknown, error: unknown) => unknown
|
||||
}
|
||||
|
||||
const emptyFallback: FallbackRender = () => null
|
||||
|
||||
export const ErrorBoundary = ({
|
||||
children,
|
||||
fallback,
|
||||
fallbackRender,
|
||||
beforeCapture,
|
||||
}: ErrorBoundaryProps) => {
|
||||
const renderFallback = fallbackRender ?? fallback ?? emptyFallback
|
||||
|
||||
const handleError = (rawError: unknown, info: { componentStack?: string | null }) => {
|
||||
const error = rawError instanceof Error ? rawError : new Error(String(rawError))
|
||||
|
||||
if (beforeCapture?.(info, error) === false) {
|
||||
return
|
||||
}
|
||||
|
||||
void tracker.manager.captureException(error, {
|
||||
source: "desktop_error_boundary",
|
||||
component_stack: info.componentStack,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<ReactErrorBoundary
|
||||
onError={handleError}
|
||||
fallbackRender={(props) =>
|
||||
renderFallback({
|
||||
...props,
|
||||
resetError: props.resetErrorBoundary,
|
||||
})
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</ReactErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Button } from "@follow/components/ui/button/index.js"
|
||||
import { captureException } from "@sentry/react"
|
||||
import { tracker } from "@follow/tracker"
|
||||
import { useEffect, useRef } from "react"
|
||||
import { isRouteErrorResponse, useNavigate, useRouteError } from "react-router"
|
||||
import { toast } from "sonner"
|
||||
|
|
@ -27,8 +27,9 @@ export function ErrorElement() {
|
|||
|
||||
useEffect(() => {
|
||||
console.error("Error handled by React Router default ErrorBoundary:", error)
|
||||
|
||||
captureException(error)
|
||||
void tracker.manager.captureException(error, {
|
||||
source: "desktop_router_error_element",
|
||||
})
|
||||
}, [error])
|
||||
|
||||
const reloadRef = useRef(false)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Logo } from "@follow/components/icons/logo.jsx"
|
||||
import { Button } from "@follow/components/ui/button/index.js"
|
||||
import { ELECTRON_BUILD } from "@follow/shared/constants"
|
||||
import { captureException } from "@sentry/react"
|
||||
import { useEffect } from "react"
|
||||
import type { Location } from "react-router"
|
||||
import { Navigate, useLocation, useNavigate } from "react-router"
|
||||
|
|
@ -33,7 +32,7 @@ export const NotFound = () => {
|
|||
if (!ELECTRON_BUILD) {
|
||||
return
|
||||
}
|
||||
captureException(
|
||||
console.error(
|
||||
new AccessNotFoundError(
|
||||
"Electron app got to a 404 page, this should not happen",
|
||||
location.pathname,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import { captureException } from "@sentry/react"
|
||||
import { tracker } from "@follow/tracker"
|
||||
import { useEffect } from "react"
|
||||
|
||||
export const BlockError = (props: { error: any; message: string }) => {
|
||||
useEffect(() => {
|
||||
captureException(props.error)
|
||||
}, [])
|
||||
console.error(props.error)
|
||||
void tracker.manager.captureException(props.error, {
|
||||
source: "desktop_markdown_block_error",
|
||||
message: props.message,
|
||||
})
|
||||
}, [props.error, props.message])
|
||||
return (
|
||||
<div className="center flex min-h-12 flex-col rounded bg-red py-4 text-sm text-white">
|
||||
{props.message}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { nextFrame } from "@follow/utils/dom"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import { ErrorBoundary } from "@sentry/react"
|
||||
import { useForceUpdate } from "motion/react"
|
||||
import type { FC, ImgHTMLAttributes, VideoHTMLAttributes } from "react"
|
||||
import * as React from "react"
|
||||
|
|
@ -11,6 +10,7 @@ import { useEventCallback } from "usehooks-ts"
|
|||
import { useGetImageProxyUrl } from "~/lib/img-proxy"
|
||||
import { saveImageDimensionsToDb } from "~/store/image/db"
|
||||
|
||||
import { ErrorBoundary } from "../../common/ErrorBoundary"
|
||||
import { useMediaContainerWidth, usePreviewMedia } from "./hooks"
|
||||
import { MediaInfoRecordContext } from "./MediaInfoRecordContext"
|
||||
import type { VideoPlayerRef } from "./VideoPlayer"
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ export const initAnalytics = async () => {
|
|||
api_host: env.VITE_POSTHOG_HOST,
|
||||
person_profiles: "identified_only",
|
||||
defaults: "2025-05-24",
|
||||
capture_exceptions: {
|
||||
capture_unhandled_errors: true,
|
||||
capture_unhandled_rejections: true,
|
||||
capture_console_errors: false,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
import { tracker } from "@follow/tracker"
|
||||
import type { AuthUser } from "@follow-app/client-sdk"
|
||||
|
||||
export const setIntegrationIdentify = async (user: AuthUser) => {
|
||||
export const setIntegrationIdentify = (user: AuthUser) => {
|
||||
tracker.identify(user)
|
||||
await import("@sentry/react").then(({ setTag }) => {
|
||||
setTag("user_id", user.id)
|
||||
setTag("user_name", user.name)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import { appLog } from "../lib/log"
|
|||
import { initAnalytics } from "./analytics"
|
||||
import { registerHistoryStack } from "./history"
|
||||
import { doMigration } from "./migrates"
|
||||
import { initSentry } from "./sentry"
|
||||
import { initializeSettings } from "./settings"
|
||||
|
||||
declare global {
|
||||
|
|
@ -82,7 +81,6 @@ export const initializeApp = async () => {
|
|||
|
||||
apm("initializeSettings", initializeSettings)
|
||||
|
||||
initSentry()
|
||||
await apm("i18n", initI18n)
|
||||
|
||||
apm("setting sync", () => {
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
import { env } from "@follow/shared/env.desktop"
|
||||
import type { BrowserOptions } from "@sentry/react"
|
||||
import {
|
||||
captureConsoleIntegration,
|
||||
eventFiltersIntegration,
|
||||
httpClientIntegration,
|
||||
moduleMetadataIntegration,
|
||||
reactRouterV6BrowserTracingIntegration,
|
||||
} from "@sentry/react"
|
||||
import { useEffect } from "react"
|
||||
import { createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from "react-router"
|
||||
|
||||
const ERROR_PATTERNS = [
|
||||
/Network Error/i,
|
||||
/Fetch Error/i,
|
||||
/XHR Error/i,
|
||||
/adsbygoogle/i,
|
||||
/Failed to fetch/i,
|
||||
"FetchError",
|
||||
"FollowAuthError",
|
||||
"fetch failed",
|
||||
"Unable to open cursor",
|
||||
"Document is not focused.",
|
||||
"Tracker",
|
||||
"HTTP Client Error",
|
||||
// Biz errors
|
||||
"Chain aborted",
|
||||
"The database connection is closing",
|
||||
"NotSupportedError",
|
||||
"Request failed",
|
||||
"The user rejected the request",
|
||||
"TypeError: Failed to fetch",
|
||||
"ResizeObserver loop completed with undelivered notifications",
|
||||
"ResizeObserver loop limit exceeded",
|
||||
"A mutation operation was attempted on a database that did not allow mutations",
|
||||
"401",
|
||||
"HTTP Client Error with status code: ",
|
||||
"DatabaseClosedError",
|
||||
"SecurityError",
|
||||
"NotFoundError",
|
||||
"Large Render Blocking Asset",
|
||||
]
|
||||
|
||||
export const SentryConfig: BrowserOptions = {
|
||||
dsn: env.VITE_SENTRY_DSN,
|
||||
environment: RELEASE_CHANNEL,
|
||||
integrations: [
|
||||
eventFiltersIntegration(),
|
||||
moduleMetadataIntegration(),
|
||||
httpClientIntegration(),
|
||||
reactRouterV6BrowserTracingIntegration({
|
||||
useEffect,
|
||||
useLocation,
|
||||
useNavigationType,
|
||||
createRoutesFromChildren,
|
||||
matchRoutes,
|
||||
}),
|
||||
captureConsoleIntegration({
|
||||
levels: ["error"],
|
||||
}),
|
||||
],
|
||||
ignoreErrors: ERROR_PATTERNS,
|
||||
// Performance Monitoring
|
||||
tracesSampleRate: 1, // Capture 100% of the transactions
|
||||
// Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
|
||||
tracePropagationTargets: ["localhost", env.VITE_API_URL],
|
||||
// Session Replay
|
||||
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
|
||||
replaysOnErrorSampleRate: 1,
|
||||
|
||||
beforeSend(event, hint) {
|
||||
const error = hint.originalException
|
||||
|
||||
if (error instanceof Error && "traceId" in error && error.traceId) {
|
||||
event.tags = {
|
||||
...event.tags,
|
||||
traceId: error.traceId as string,
|
||||
}
|
||||
}
|
||||
|
||||
return event
|
||||
},
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import { ELECTRON_BUILD } from "@follow/shared/constants"
|
||||
import { whoami } from "@follow/store/user/getters"
|
||||
import { appSessionTraceId } from "@follow/utils/environment"
|
||||
import { version } from "@pkg"
|
||||
import { nanoid } from "nanoid"
|
||||
|
||||
import { SentryConfig } from "./sentry.config"
|
||||
|
||||
Object.defineProperty(window.Error.prototype, "traceId", {
|
||||
get() {
|
||||
if (!this._traceId) {
|
||||
this._traceId = nanoid()
|
||||
}
|
||||
return this._traceId
|
||||
},
|
||||
})
|
||||
export const initSentry = async () => {
|
||||
if (!window.SENTRY_RELEASE) return
|
||||
if (import.meta.env.DEV) return
|
||||
const Sentry = await import("@sentry/react")
|
||||
Sentry.init(SentryConfig)
|
||||
|
||||
const user = whoami()
|
||||
if (user) {
|
||||
Sentry.setTag("user_id", user.id)
|
||||
Sentry.setTag("user_name", user.name)
|
||||
}
|
||||
|
||||
Sentry.setTag("session_trace_id", appSessionTraceId)
|
||||
Sentry.setTag("app_version", version)
|
||||
Sentry.setTag("build", ELECTRON_BUILD ? "electron" : "web")
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ export const getNewIssueUrl = ({
|
|||
if (title) searchParams.set("title", title)
|
||||
|
||||
if (error && "traceId" in error && error.traceId) {
|
||||
nextBody += `\n\n### Sentry Trace ID\n${error.traceId}`
|
||||
nextBody += `\n\n### Trace ID\n${error.traceId}`
|
||||
}
|
||||
|
||||
searchParams.set("body", nextBody)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { cn } from "@follow/utils"
|
||||
import { ErrorBoundary } from "@sentry/react"
|
||||
import { createElement, lazy, Suspense } from "react"
|
||||
|
||||
import { ErrorBoundary } from "~/components/common/ErrorBoundary"
|
||||
|
||||
const AISplineLoader = lazy(() =>
|
||||
import("./AISplineLoader").then((res) => ({ default: res.AISplineLoader })),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Button } from "@follow/components/ui/button/index.js"
|
||||
import type { FallbackRender } from "@sentry/react"
|
||||
|
||||
import type { FallbackRender } from "~/components/common/ErrorBoundary"
|
||||
import { attachOpenInEditor } from "~/lib/dev"
|
||||
|
||||
import { FeedbackIssue } from "../../../../components/common/ErrorElement"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { useUserRole } from "@follow/store/user/hooks"
|
|||
import { tracker } from "@follow/tracker"
|
||||
import { detectIsEditableElement, nextFrame } from "@follow/utils"
|
||||
import type { ConfigResponse } from "@follow-app/client-sdk"
|
||||
import { ErrorBoundary } from "@sentry/react"
|
||||
import type { EditorState } from "lexical"
|
||||
import { createEditor } from "lexical"
|
||||
import { nanoid } from "nanoid"
|
||||
|
|
@ -21,6 +20,7 @@ import { useEventCallback, useEventListener } from "usehooks-ts"
|
|||
|
||||
import { useAISettingKey } from "~/atoms/settings/ai"
|
||||
import { useActionLanguage } from "~/atoms/settings/general"
|
||||
import { ErrorBoundary } from "~/components/common/ErrorBoundary"
|
||||
import { ROUTE_FEED_IN_FOLDER } from "~/constants"
|
||||
import { getRouteParams } from "~/hooks/biz/useRouteParams"
|
||||
import { useRequireLogin } from "~/hooks/common/useRequireLogin"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import "@xyflow/react/dist/style.css"
|
||||
|
||||
import { alwaysFalse } from "@follow/utils"
|
||||
import { ErrorBoundary } from "@sentry/react"
|
||||
import type { ReasoningUIPart, TextUIPart, ToolUIPart } from "ai"
|
||||
import * as React from "react"
|
||||
|
||||
import { ErrorBoundary } from "~/components/common/ErrorBoundary"
|
||||
import type { AIDisplayFlowTool, BizUIMessage, BizUITools } from "~/modules/ai-chat/store/types"
|
||||
|
||||
import { useChatStatus } from "../../store/hooks"
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import { useIsInbox } from "@follow/store/inbox/hooks"
|
|||
import { thenable } from "@follow/utils"
|
||||
import { stopPropagation } from "@follow/utils/dom"
|
||||
import { clsx } from "@follow/utils/utils"
|
||||
import { ErrorBoundary } from "@sentry/react"
|
||||
import * as React from "react"
|
||||
import { memo } from "react"
|
||||
|
||||
import { useEntryIsInReadability } from "~/atoms/readability"
|
||||
import { useUISettingKey } from "~/atoms/settings/ui"
|
||||
import { ErrorBoundary } from "~/components/common/ErrorBoundary"
|
||||
import { ShadowDOM } from "~/components/common/ShadowDOM"
|
||||
import { useRenderStyle } from "~/hooks/biz/useRenderStyle"
|
||||
import { useRouteParamsSelector } from "~/hooks/biz/useRouteParams"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Button } from "@follow/components/ui/button/index.js"
|
||||
import type { FallbackRender } from "@sentry/react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import type { FallbackRender } from "~/components/common/ErrorBoundary"
|
||||
import { getNewIssueUrl } from "~/lib/issues"
|
||||
|
||||
export const EntryRenderError: FallbackRender = ({ error }) => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { useEntry } from "@follow/store/entry/hooks"
|
|||
import { useFeedById } from "@follow/store/feed/hooks"
|
||||
import { useIsInbox } from "@follow/store/inbox/hooks"
|
||||
import { cn } from "@follow/utils"
|
||||
import { ErrorBoundary } from "@sentry/react"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
|
||||
import {
|
||||
|
|
@ -15,6 +14,7 @@ import {
|
|||
useAIPanelVisibility,
|
||||
} from "~/atoms/settings/ai"
|
||||
import { useUISettingKey } from "~/atoms/settings/ui"
|
||||
import { ErrorBoundary } from "~/components/common/ErrorBoundary"
|
||||
import { ShadowDOM } from "~/components/common/ShadowDOM"
|
||||
import type { TocRef } from "~/components/ui/markdown/components/Toc"
|
||||
import { useInPeekModal } from "~/components/ui/modal/inspire/InPeekModal"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { IN_ELECTRON } from "@follow/shared/constants"
|
||||
import { wrapCreateBrowserRouterV7 } from "@sentry/react"
|
||||
import { createBrowserRouter, createHashRouter } from "react-router"
|
||||
|
||||
import { Component as App } from "./App"
|
||||
|
|
@ -8,11 +7,8 @@ import { NotFound } from "./components/common/NotFound"
|
|||
// @ts-ignore
|
||||
import { routes as tree } from "./generated-routes"
|
||||
|
||||
let routerCreator =
|
||||
const routerCreator =
|
||||
IN_ELECTRON || globalThis["__DEBUG_PROXY__"] ? createHashRouter : createBrowserRouter
|
||||
if (window.SENTRY_RELEASE) {
|
||||
routerCreator = wrapCreateBrowserRouterV7(routerCreator)
|
||||
}
|
||||
|
||||
export const router = routerCreator([
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { IN_ELECTRON } from "@follow/shared/constants"
|
||||
import { wrapCreateBrowserRouterV7 } from "@sentry/react"
|
||||
import { createBrowserRouter, createHashRouter } from "react-router"
|
||||
|
||||
import { ErrorElement } from "./components/common/ErrorElement"
|
||||
|
|
@ -7,11 +6,8 @@ import { NotFound } from "./components/common/NotFound"
|
|||
// @ts-ignore
|
||||
import { routes as tree } from "./generated-routes"
|
||||
|
||||
let routerCreator =
|
||||
const routerCreator =
|
||||
IN_ELECTRON || globalThis["__DEBUG_PROXY__"] ? createHashRouter : createBrowserRouter
|
||||
if (window.SENTRY_RELEASE) {
|
||||
routerCreator = wrapCreateBrowserRouterV7(routerCreator)
|
||||
}
|
||||
|
||||
export const router = routerCreator([
|
||||
{
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@
|
|||
"@follow/shared": "workspace:*",
|
||||
"@follow/utils": "workspace:*",
|
||||
"@pengx17/electron-forge-maker-appimage": "1.2.1",
|
||||
"@sentry/vite-plugin": "4.9.1",
|
||||
"@types/html-minifier-terser": "7.0.2",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@vitejs/plugin-legacy": "7.2.1",
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ export default ({ mode }) => {
|
|||
],
|
||||
["tldts"],
|
||||
|
||||
["@sentry/react", "@openpanel/web"],
|
||||
["@openpanel/web"],
|
||||
["zod", "react-hook-form", "@hookform/resolvers"],
|
||||
]),
|
||||
|
||||
|
|
|
|||
|
|
@ -137,14 +137,6 @@ export default ({ config }: ConfigContext): ExpoConfig => {
|
|||
require("./plugins/with-android-manifest-plugin.js"),
|
||||
"expo-secure-store",
|
||||
"@react-native-firebase/app",
|
||||
[
|
||||
"@sentry/react-native/expo",
|
||||
{
|
||||
url: "https://sentry.io/",
|
||||
project: "react-native",
|
||||
organization: "follow-rg",
|
||||
},
|
||||
],
|
||||
[
|
||||
"expo-image-picker",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -170,7 +170,6 @@
|
|||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
||||
6E4ADCBDA5B760296BAE78FA /* [CP] Embed Pods Frameworks */,
|
||||
8F2D99FDEA6998C9D98BD7E0 /* [CP-User] [RNFB] Core Configuration */,
|
||||
3B0B406257C84B7586048134 /* Upload Debug Symbols to Sentry */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -243,7 +242,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n/bin/sh `\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode.sh'\"` `\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
|
||||
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n/bin/sh `\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
|
||||
};
|
||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
|
|
@ -267,20 +266,6 @@
|
|||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3B0B406257C84B7586048134 /* Upload Debug Symbols to Sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Upload Debug Symbols to Sentry";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh ../../../node_modules/@sentry/react-native/scripts/sentry-xcode-debug-files.sh\n";
|
||||
};
|
||||
6E4ADCBDA5B760296BAE78FA /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -335,7 +320,6 @@
|
|||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/Sentry/Sentry.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit_Privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
|
||||
|
|
@ -374,7 +358,6 @@
|
|||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Sentry.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SnapKit_Privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
defaults.url=https://sentry.io/
|
||||
defaults.org=follow-rg
|
||||
defaults.project=react-native
|
||||
# Using SENTRY_AUTH_TOKEN environment variable
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
const { getDefaultConfig } = require("expo/metro-config")
|
||||
const { withNativeWind } = require("nativewind/metro")
|
||||
const path = require("pathe")
|
||||
const { wrapWithReanimatedMetroConfig } = require("react-native-reanimated/metro-config")
|
||||
const { getSentryExpoConfig } = require("@sentry/react-native/metro")
|
||||
|
||||
const config = getSentryExpoConfig(__dirname, { isCSSEnabled: true })
|
||||
const config = getDefaultConfig(__dirname, { isCSSEnabled: true })
|
||||
const workspaceRoot = path.resolve(__dirname, "../..")
|
||||
config.resolver.sourceExts.push("sql")
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@
|
|||
"@react-native-masked-view/masked-view": "0.3.2",
|
||||
"@react-native-menu/menu": "2.0.0",
|
||||
"@react-native-picker/picker": "2.11.1",
|
||||
"@sentry/react-native": "7.2.0",
|
||||
"@shopify/flash-list": "2.0.2",
|
||||
"@tanstack/query-sync-storage-persister": "5.90.22",
|
||||
"@tanstack/react-query": "5.90.21",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useTypeScriptHappyCallback } from "@follow/hooks"
|
||||
import { captureException } from "@sentry/react-native"
|
||||
import { tracker } from "@follow/tracker"
|
||||
import type { FC } from "react"
|
||||
import { createElement, useEffect } from "react"
|
||||
import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary"
|
||||
|
|
@ -52,8 +52,10 @@ const defaultFallbackRender = ({ error }: { error: Error }) => {
|
|||
}
|
||||
const ErrorReport = ({ error }: { error: Error }) => {
|
||||
useEffect(() => {
|
||||
captureException(error)
|
||||
console.error(error)
|
||||
void tracker.manager.captureException(error, {
|
||||
source: "mobile_error_boundary",
|
||||
})
|
||||
}, [error])
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,19 @@ import { proxyEnv } from "../lib/proxy-env"
|
|||
export const initAnalytics = async () => {
|
||||
setFirebaseTracker(getAnalytics())
|
||||
|
||||
const user = whoami()
|
||||
if (user) {
|
||||
tracker.identify(user as AuthUser)
|
||||
if (proxyEnv.POSTHOG_KEY) {
|
||||
setPostHogTracker(
|
||||
new PostHog(proxyEnv.POSTHOG_KEY, {
|
||||
host: proxyEnv.POSTHOG_HOST,
|
||||
errorTracking: {
|
||||
autocapture: {
|
||||
uncaughtExceptions: true,
|
||||
unhandledRejections: true,
|
||||
console: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
tracker.manager.appendUserProperties({
|
||||
|
|
@ -21,8 +31,9 @@ export const initAnalytics = async () => {
|
|||
buildId: nativeBuildVersion,
|
||||
})
|
||||
|
||||
if (proxyEnv.POSTHOG_KEY) {
|
||||
setPostHogTracker(new PostHog(proxyEnv.POSTHOG_KEY))
|
||||
const user = whoami()
|
||||
if (user) {
|
||||
tracker.identify(user as AuthUser)
|
||||
}
|
||||
|
||||
// op.setGlobalProperties({
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import { initDeviceType } from "./device"
|
|||
import { hydrateQueryClient, hydrateSettings } from "./hydrate"
|
||||
import { migrateDatabase } from "./migration"
|
||||
import { initializePlayer } from "./player"
|
||||
import { initializeSentry } from "./sentry"
|
||||
|
||||
/* eslint-disable no-console */
|
||||
export const initializeApp = async () => {
|
||||
|
|
@ -21,8 +20,6 @@ export const initializeApp = async () => {
|
|||
|
||||
const now = Date.now()
|
||||
|
||||
initializeSentry()
|
||||
|
||||
await initDeviceType()
|
||||
await initializeDB()
|
||||
void apm("migrateLegacyApiSession", migrateLegacyApiSession).catch((error) => {
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
import * as Sentry from "@sentry/react-native"
|
||||
|
||||
export const initializeSentry = () => {
|
||||
// Unlike Sentry on other platforms, you do not need to import anything to use tracing on React Native
|
||||
Sentry.init({
|
||||
dsn: "https://cbfecd786e09a9481676655a4da88a7e@o4507542488023040.ingest.us.sentry.io/4509926421102593",
|
||||
// We recommend adjusting this value in production, or using tracesSampler
|
||||
// for finer control
|
||||
tracesSampleRate: 1,
|
||||
})
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ import "./global.css"
|
|||
import "./polyfill"
|
||||
|
||||
import { apiContext, authClientContext, queryClientContext } from "@follow/store/context"
|
||||
import * as Sentry from "@sentry/react-native"
|
||||
import { registerRootComponent } from "expo"
|
||||
import { Image } from "expo-image"
|
||||
import { LinearGradient } from "expo-linear-gradient"
|
||||
|
|
@ -45,7 +44,7 @@ enableFreeze(true)
|
|||
initializeApp()
|
||||
registerSitemap()
|
||||
initializeI18n()
|
||||
registerRootComponent(Sentry.wrap(RootComponent))
|
||||
registerRootComponent(RootComponent)
|
||||
|
||||
function RootComponent() {
|
||||
const { t } = useTranslation()
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ export type TrackPayload = {
|
|||
properties?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type CaptureExceptionPayload = {
|
||||
error: unknown
|
||||
properties?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface TrackerAdapter {
|
||||
/**
|
||||
* Initialize the tracker adapter
|
||||
|
|
@ -22,6 +27,11 @@ export interface TrackerAdapter {
|
|||
*/
|
||||
track: (payload: TrackPayload) => Promise<void> | void
|
||||
|
||||
/**
|
||||
* Capture an exception
|
||||
*/
|
||||
captureException?: (payload: CaptureExceptionPayload) => Promise<void> | void
|
||||
|
||||
/**
|
||||
* Identify a user
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export type { IdentifyPayload, TrackerAdapter, TrackPayload } from "./base"
|
||||
export type { CaptureExceptionPayload, IdentifyPayload, TrackerAdapter, TrackPayload } from "./base"
|
||||
export { FirebaseAdapter, type FirebaseAdapterConfig } from "./firebase"
|
||||
export { OpenPanelAdapter, type OpenPanelAdapterConfig } from "./openpanel"
|
||||
export { PostHogAdapter, type PostHogAdapterConfig } from "./posthog"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { PostHog } from "posthog-js"
|
||||
import type PostHogReactNative from "posthog-react-native"
|
||||
|
||||
import type { IdentifyPayload, TrackerAdapter, TrackPayload } from "./base"
|
||||
import type { CaptureExceptionPayload, IdentifyPayload, TrackerAdapter, TrackPayload } from "./base"
|
||||
|
||||
export interface PostHogAdapterConfig {
|
||||
instance: PostHog | PostHogReactNative
|
||||
|
|
@ -31,6 +31,19 @@ export class PostHogAdapter implements TrackerAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
async captureException({ error, properties }: CaptureExceptionPayload): Promise<void> {
|
||||
if (!this.isEnabled()) return
|
||||
|
||||
try {
|
||||
this.posthogInstance.captureException(
|
||||
error,
|
||||
properties as Parameters<typeof this.posthogInstance.captureException>[1],
|
||||
)
|
||||
} catch (captureError) {
|
||||
console.error("[PostHog] Failed to capture exception:", captureError)
|
||||
}
|
||||
}
|
||||
|
||||
async identify(payload: IdentifyPayload): Promise<void> {
|
||||
if (!this.isEnabled()) return
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export const setProxyTracker = improvedTrackManager.setProxyTracker.bind(improve
|
|||
export const tracker = new TrackerPoints()
|
||||
|
||||
export {
|
||||
type CaptureExceptionPayload,
|
||||
FirebaseAdapter,
|
||||
type FirebaseAdapterConfig,
|
||||
type IdentifyPayload,
|
||||
|
|
|
|||
|
|
@ -93,6 +93,33 @@ export class TrackerManager {
|
|||
await this.executeTrackingForAdapters(enabledAdapters, payload)
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture an exception across all enabled adapters
|
||||
*/
|
||||
async captureException(error: unknown, properties?: Record<string, unknown>): Promise<void> {
|
||||
const enabledAdapters = this.getEnabledAdapters()
|
||||
|
||||
if (enabledAdapters.length === 0) {
|
||||
console.warn("[TrackerManager] No enabled adapters found for exception capture")
|
||||
return
|
||||
}
|
||||
|
||||
const promises = enabledAdapters.map(async (adapter) => {
|
||||
if (!adapter.captureException) return
|
||||
|
||||
try {
|
||||
await Promise.resolve(adapter.captureException({ error, properties }))
|
||||
} catch (captureError) {
|
||||
console.error(
|
||||
`[TrackerManager] Failed to capture exception with adapter "${adapter.getName()}":`,
|
||||
captureError,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.allSettled(promises)
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify a user across all enabled adapters
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
diff --git a/esm/main/ipc.js b/esm/main/ipc.js
|
||||
index 2fdc06044866a4f4c3420bd8b4a5d2788faa3e4e..7f0250b911871aa12e379d848a6a5b5668bdb282 100644
|
||||
--- a/esm/main/ipc.js
|
||||
+++ b/esm/main/ipc.js
|
||||
@@ -130,18 +130,6 @@ function configureProtocol(client, ipcUtil, options) {
|
||||
if (app.isReady()) {
|
||||
throw new Error("Sentry SDK should be initialized before the Electron app 'ready' event is fired");
|
||||
}
|
||||
- const scheme = {
|
||||
- scheme: ipcUtil.namespace,
|
||||
- privileges: { bypassCSP: true, corsEnabled: true, supportFetchAPI: true, secure: true },
|
||||
- };
|
||||
- protocol.registerSchemesAsPrivileged([scheme]);
|
||||
- // We Proxy this function so that later user calls to registerSchemesAsPrivileged don't overwrite our custom scheme
|
||||
- // eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
- protocol.registerSchemesAsPrivileged = new Proxy(protocol.registerSchemesAsPrivileged, {
|
||||
- apply: (target, __, args) => {
|
||||
- target([...args[0], scheme]);
|
||||
- },
|
||||
- });
|
||||
const rendererStatusChanged = createRendererEventLoopBlockStatusHandler(client);
|
||||
app
|
||||
.whenReady()
|
||||
diff --git a/main/ipc.js b/main/ipc.js
|
||||
index 3cdf12e818890effadc0d5188fe104549c846b97..597ea1aabc2314e306864c562d9609541fc3edfe 100644
|
||||
--- a/main/ipc.js
|
||||
+++ b/main/ipc.js
|
||||
@@ -130,18 +130,6 @@ function configureProtocol(client, ipcUtil, options) {
|
||||
if (electron.app.isReady()) {
|
||||
throw new Error("Sentry SDK should be initialized before the Electron app 'ready' event is fired");
|
||||
}
|
||||
- const scheme = {
|
||||
- scheme: ipcUtil.namespace,
|
||||
- privileges: { bypassCSP: true, corsEnabled: true, supportFetchAPI: true, secure: true },
|
||||
- };
|
||||
- electron.protocol.registerSchemesAsPrivileged([scheme]);
|
||||
- // We Proxy this function so that later user calls to registerSchemesAsPrivileged don't overwrite our custom scheme
|
||||
- // eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
- electron.protocol.registerSchemesAsPrivileged = new Proxy(electron.protocol.registerSchemesAsPrivileged, {
|
||||
- apply: (target, __, args) => {
|
||||
- target([...args[0], scheme]);
|
||||
- },
|
||||
- });
|
||||
const rendererStatusChanged = rendererAnr.createRendererEventLoopBlockStatusHandler(client);
|
||||
electron.app
|
||||
.whenReady()
|
||||
1060
pnpm-lock.yaml
1060
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -17,7 +17,6 @@ ignorePatchFailures: false
|
|||
|
||||
onlyBuiltDependencies:
|
||||
- "@firebase/util"
|
||||
- "@sentry/cli"
|
||||
- "@tsslint/core"
|
||||
- "@tsslint/eslint"
|
||||
- bufferutil
|
||||
|
|
@ -59,7 +58,6 @@ patchedDependencies:
|
|||
"@microflash/remark-callout-directives": patches/@microflash__remark-callout-directives.patch
|
||||
"@mozilla/readability@0.6.0": patches/@mozilla__readability@0.6.0.patch
|
||||
"@pengx17/electron-forge-maker-appimage": patches/@pengx17__electron-forge-maker-appimage.patch
|
||||
"@sentry/electron": patches/@sentry__electron.patch
|
||||
daisyui@4.12.24: patches/daisyui@4.12.24.patch
|
||||
re-resizable@6.11.2: patches/re-resizable@6.11.2.patch
|
||||
react-native-sheet-transitions: patches/react-native-sheet-transitions.patch
|
||||
|
|
|
|||
Loading…
Reference in New Issue