Call core dump from the bug reporting button(s) (#2783)

*  Add coredump to refresh button - this one indicates that there should be something like a core dump that is triggered.
* Added lower right control bug report button - included custom toasts for bug reporting, supports fallback bug reporting when app cannot generate a core dump
This commit is contained in:
Dan Shaw
2024-06-28 18:06:40 -07:00
committed by GitHub
parent 6fccc68c18
commit f86473d13b
7 changed files with 104 additions and 10 deletions

4
src-tauri/Cargo.lock generated
View File

@ -4546,9 +4546,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.116" version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
dependencies = [ dependencies = [
"indexmap 2.2.6", "indexmap 2.2.6",
"itoa 1.0.11", "itoa 1.0.11",

View File

@ -25,6 +25,7 @@ import { LowerRightControls } from 'components/LowerRightControls'
import ModalContainer from 'react-modal-promise' import ModalContainer from 'react-modal-promise'
import useHotkeyWrapper from 'lib/hotkeyWrapper' import useHotkeyWrapper from 'lib/hotkeyWrapper'
import Gizmo from 'components/Gizmo' import Gizmo from 'components/Gizmo'
import { CoreDumpManager } from 'lib/coredump'
export function App() { export function App() {
useRefreshSettings(paths.FILE + 'SETTINGS') useRefreshSettings(paths.FILE + 'SETTINGS')
@ -55,7 +56,11 @@ export function App() {
setHtmlRef(ref) setHtmlRef(ref)
}, [ref]) }, [ref])
const { settings } = useSettingsAuthContext() const { auth, settings } = useSettingsAuthContext()
const token = auth?.context?.token
const coreDumpManager = new CoreDumpManager(engineCommandManager, ref, token)
const { const {
app: { onboardingStatus }, app: { onboardingStatus },
} = settings.context } = settings.context
@ -129,7 +134,7 @@ export function App() {
<ModelingSidebar paneOpacity={paneOpacity} /> <ModelingSidebar paneOpacity={paneOpacity} />
<Stream /> <Stream />
{/* <CamToggle /> */} {/* <CamToggle /> */}
<LowerRightControls> <LowerRightControls coreDumpManager={coreDumpManager}>
<Gizmo /> <Gizmo />
</LowerRightControls> </LowerRightControls>
</div> </div>

View File

@ -6,8 +6,18 @@ import { NetworkHealthIndicator } from 'components/NetworkHealthIndicator'
import { HelpMenu } from './HelpMenu' import { HelpMenu } from './HelpMenu'
import { Link, useLocation } from 'react-router-dom' import { Link, useLocation } from 'react-router-dom'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
import { coreDump } from 'lang/wasm'
import toast from 'react-hot-toast'
import { CoreDumpManager } from 'lib/coredump'
import openWindow from 'lib/openWindow'
export function LowerRightControls(props: React.PropsWithChildren) { export function LowerRightControls({
children,
coreDumpManager,
}: {
children?: React.ReactNode
coreDumpManager?: CoreDumpManager
}) {
const location = useLocation() const location = useLocation()
const filePath = useAbsoluteFilePath() const filePath = useAbsoluteFilePath()
const linkOverrideClassName = const linkOverrideClassName =
@ -15,9 +25,42 @@ export function LowerRightControls(props: React.PropsWithChildren) {
const isPlayWright = window?.localStorage.getItem('playwright') === 'true' const isPlayWright = window?.localStorage.getItem('playwright') === 'true'
async function reportbug(event: { preventDefault: () => void }) {
event?.preventDefault()
if (!coreDumpManager) {
// open default reporting option
openWindow('https://github.com/KittyCAD/modeling-app/issues/new/choose')
} else {
toast
.promise(
coreDump(coreDumpManager, true),
{
loading: 'Preparing bug report...',
success: 'Bug report opened in new window',
error: 'Unable to export a core dump. Using default reporting.',
},
{
success: {
// Note: this extended duration is especially important for Playwright e2e testing
// default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
duration: 6000,
},
}
)
.catch((err: Error) => {
if (err) {
openWindow(
'https://github.com/KittyCAD/modeling-app/issues/new/choose'
)
}
})
}
}
return ( return (
<section className="fixed bottom-2 right-2 flex flex-col items-end gap-3 pointer-events-none"> <section className="fixed bottom-2 right-2 flex flex-col items-end gap-3 pointer-events-none">
{props.children} {children}
<menu className="flex items-center justify-end gap-3 pointer-events-auto"> <menu className="flex items-center justify-end gap-3 pointer-events-auto">
<a <a
href={`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`} href={`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`}
@ -28,6 +71,7 @@ export function LowerRightControls(props: React.PropsWithChildren) {
v{isPlayWright ? '11.22.33' : APP_VERSION} v{isPlayWright ? '11.22.33' : APP_VERSION}
</a> </a>
<a <a
onClick={reportbug}
href="https://github.com/KittyCAD/modeling-app/issues/new/choose" href="https://github.com/KittyCAD/modeling-app/issues/new/choose"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"

View File

@ -124,7 +124,6 @@ export const ModelingMachineProvider = ({
token token
) )
useHotkeyWrapper(['meta + shift + .'], () => { useHotkeyWrapper(['meta + shift + .'], () => {
console.warn('CoreDump: Initializing core dump')
toast.promise( toast.promise(
coreDump(coreDumpManager, true), coreDump(coreDumpManager, true),
{ {

View File

@ -1,7 +1,25 @@
import { coreDump } from 'lang/wasm'
import { CoreDumpManager } from 'lib/coredump'
import { CustomIcon } from './CustomIcon' import { CustomIcon } from './CustomIcon'
import { engineCommandManager } from 'lib/singletons'
import React from 'react'
import toast from 'react-hot-toast'
import Tooltip from './Tooltip' import Tooltip from './Tooltip'
import { useStore } from 'useStore'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
export const RefreshButton = ({ children }: React.PropsWithChildren) => {
const { auth } = useSettingsAuthContext()
const token = auth?.context?.token
const { htmlRef } = useStore((s) => ({
htmlRef: s.htmlRef,
}))
const coreDumpManager = new CoreDumpManager(
engineCommandManager,
htmlRef,
token
)
export function RefreshButton() {
async function refresh() { async function refresh() {
if (window && 'plausible' in window) { if (window && 'plausible' in window) {
const p = window.plausible as ( const p = window.plausible as (
@ -17,8 +35,26 @@ export function RefreshButton() {
}) })
} }
toast
.promise(
coreDump(coreDumpManager, true),
{
loading: 'Starting core dump...',
success: 'Core dump completed successfully',
error: 'Error while exporting core dump',
},
{
success: {
// Note: this extended duration is especially important for Playwright e2e testing
// default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
duration: 6000,
},
}
)
.then(() => {
// Window may not be available in some environments // Window may not be available in some environments
window?.location.reload() window?.location.reload()
})
} }
return ( return (

View File

@ -334,6 +334,7 @@ export async function coreDump(
openGithubIssue: boolean = false openGithubIssue: boolean = false
): Promise<CoreDumpInfo> { ): Promise<CoreDumpInfo> {
try { try {
console.warn('CoreDump: Initializing core dump')
const dump: CoreDumpInfo = await coredump(coreDumpManager) const dump: CoreDumpInfo = await coredump(coreDumpManager)
/* NOTE: this console output of the coredump should include the field /* NOTE: this console output of the coredump should include the field
`github_issue_url` which is not in the uploaded coredump file. `github_issue_url` which is not in the uploaded coredump file.

View File

@ -13,6 +13,14 @@ import screenshot from 'lib/screenshot'
import React from 'react' import React from 'react'
import { VITE_KC_API_BASE_URL } from 'env' import { VITE_KC_API_BASE_URL } from 'env'
/* eslint-disable suggest-no-throw/suggest-no-throw --
* All the throws in CoreDumpManager are intentional and should be caught and handled properly
* by the calling Promises with a catch block. The throws are essential to properly handling
* when the app isn't ready enough or otherwise unable to produce a core dump. By throwing
* instead of simply erroring, the code halts execution at the first point which it cannot
* complete the core dump request.
**/
/** /**
* CoreDumpManager module * CoreDumpManager module
* - for getting all the values from the JS world to pass to the Rust world for a core dump. * - for getting all the values from the JS world to pass to the Rust world for a core dump.
@ -22,6 +30,7 @@ import { VITE_KC_API_BASE_URL } from 'env'
// CoreDumpManager is instantiated in ModelingMachineProvider and passed to coreDump() in wasm.ts // CoreDumpManager is instantiated in ModelingMachineProvider and passed to coreDump() in wasm.ts
// The async function coreDump() handles any errors thrown in its Promise catch method and rethrows // The async function coreDump() handles any errors thrown in its Promise catch method and rethrows
// them to so the toast handler in ModelingMachineProvider can show the user an error message toast // them to so the toast handler in ModelingMachineProvider can show the user an error message toast
// TODO: Throw more
export class CoreDumpManager { export class CoreDumpManager {
engineCommandManager: EngineCommandManager engineCommandManager: EngineCommandManager
htmlRef: React.RefObject<HTMLDivElement> | null htmlRef: React.RefObject<HTMLDivElement> | null