kopia lustrzana https://github.com/mifi/lossless-cut
add more error reporting #277
rodzic
4b80e358ce
commit
1d306e7073
58
src/App.jsx
58
src/App.jsx
|
@ -48,8 +48,9 @@ import { save as edlStoreSave, load as edlStoreLoad } from './edlStore';
|
||||||
import {
|
import {
|
||||||
getOutPath, formatDuration, toast, errorToast, showFfmpegFail, setFileNameTitle,
|
getOutPath, formatDuration, toast, errorToast, showFfmpegFail, setFileNameTitle,
|
||||||
promptTimeOffset, generateColor, getOutDir, withBlur, checkDirWriteAccess, dirExists,
|
promptTimeOffset, generateColor, getOutDir, withBlur, checkDirWriteAccess, dirExists,
|
||||||
openDirToast, askForHtml5ifySpeed,
|
openDirToast, askForHtml5ifySpeed, isMasBuild, isStoreBuild,
|
||||||
} from './util';
|
} from './util';
|
||||||
|
import { openSendReportDialog } from './reporting';
|
||||||
|
|
||||||
|
|
||||||
import loadingLottie from './7077-magic-flow.json';
|
import loadingLottie from './7077-magic-flow.json';
|
||||||
|
@ -61,7 +62,6 @@ const trash = window.require('trash');
|
||||||
const { unlink, exists } = window.require('fs-extra');
|
const { unlink, exists } = window.require('fs-extra');
|
||||||
const { extname } = window.require('path');
|
const { extname } = window.require('path');
|
||||||
|
|
||||||
|
|
||||||
const { dialog, app } = electron.remote;
|
const { dialog, app } = electron.remote;
|
||||||
|
|
||||||
const ReactSwal = withReactContent(Swal);
|
const ReactSwal = withReactContent(Swal);
|
||||||
|
@ -109,9 +109,6 @@ const calcShouldShowKeyframes = (zoomedDuration) => (zoomedDuration != null && z
|
||||||
|
|
||||||
const commonFormats = ['mov', 'mp4', 'matroska', 'mp3', 'ipod'];
|
const commonFormats = ['mov', 'mp4', 'matroska', 'mp3', 'ipod'];
|
||||||
|
|
||||||
const isMasBuild = window.process.mas;
|
|
||||||
const isStoreBuild = isMasBuild || window.process.windowsStore;
|
|
||||||
|
|
||||||
// TODO flex
|
// TODO flex
|
||||||
const topBarHeight = 32;
|
const topBarHeight = 32;
|
||||||
const timelineHeight = 36;
|
const timelineHeight = 36;
|
||||||
|
@ -892,34 +889,8 @@ const App = memo(() => {
|
||||||
const outSegments = useMemo(() => (invertCutSegments ? inverseCutSegments : apparentCutSegments),
|
const outSegments = useMemo(() => (invertCutSegments ? inverseCutSegments : apparentCutSegments),
|
||||||
[invertCutSegments, inverseCutSegments, apparentCutSegments]);
|
[invertCutSegments, inverseCutSegments, apparentCutSegments]);
|
||||||
|
|
||||||
const openSendReportDialog = useCallback(async (err) => {
|
const openSendReportDialogWithState = useCallback(async (err) => {
|
||||||
const reportInstructions = isStoreBuild
|
const state = {
|
||||||
? <p>Please send an email to <span style={{ fontWeight: 'bold' }} role="button" onClick={() => electron.shell.openExternal('mailto:losslesscut@yankee.no')}>losslesscut@yankee.no</span> where you describe what you were doing.</p>
|
|
||||||
: <p>Please create an issue at <span style={{ fontWeight: 'bold' }} role="button" onClick={() => electron.shell.openExternal('https://github.com/mifi/lossless-cut/issues')}>https://github.com/mifi/lossless-cut/issues</span> where you describe what you were doing.</p>;
|
|
||||||
|
|
||||||
ReactSwal.fire({
|
|
||||||
showCloseButton: true,
|
|
||||||
title: i18n.t('Send problem report'),
|
|
||||||
html: (
|
|
||||||
<div style={{ textAlign: 'left', overflow: 'auto', maxHeight: 300, overflowY: 'auto' }}>
|
|
||||||
{reportInstructions}
|
|
||||||
|
|
||||||
<p>Include the following text:</p>
|
|
||||||
|
|
||||||
<div style={{ fontWeight: 600, fontSize: 12, whiteSpace: 'pre-wrap' }} contentEditable suppressContentEditableWarning>
|
|
||||||
{`${err ? err.message : ''}\n\n${JSON.stringify({
|
|
||||||
err: err && {
|
|
||||||
code: err.code,
|
|
||||||
killed: err.killed,
|
|
||||||
failed: err.failed,
|
|
||||||
timedOut: err.timedOut,
|
|
||||||
isCanceled: err.isCanceled,
|
|
||||||
exitCode: err.exitCode,
|
|
||||||
signal: err.signal,
|
|
||||||
signalDescription: err.signalDescription,
|
|
||||||
},
|
|
||||||
|
|
||||||
state: {
|
|
||||||
filePath,
|
filePath,
|
||||||
fileFormat,
|
fileFormat,
|
||||||
externalStreamFiles,
|
externalStreamFiles,
|
||||||
|
@ -929,12 +900,9 @@ const App = memo(() => {
|
||||||
fileFormatData,
|
fileFormatData,
|
||||||
rotation,
|
rotation,
|
||||||
shortestFlag,
|
shortestFlag,
|
||||||
},
|
};
|
||||||
}, null, 2)}`}
|
|
||||||
</div>
|
openSendReportDialog(err, state);
|
||||||
</div>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}, [copyStreamIdsByFile, cutSegments, externalStreamFiles, fileFormat, fileFormatData, filePath, mainStreams, rotation, shortestFlag]);
|
}, [copyStreamIdsByFile, cutSegments, externalStreamFiles, fileFormat, fileFormatData, filePath, mainStreams, rotation, shortestFlag]);
|
||||||
|
|
||||||
const handleCutFailed = useCallback(async (err) => {
|
const handleCutFailed = useCallback(async (err) => {
|
||||||
|
@ -955,9 +923,9 @@ const App = memo(() => {
|
||||||
const { value } = await ReactSwal.fire({ title: i18n.t('Unable to export this file'), html, timer: null, showConfirmButton: true, showCancelButton: true, confirmButtonText: i18n.t('OK'), cancelButtonText: i18n.t('Report') });
|
const { value } = await ReactSwal.fire({ title: i18n.t('Unable to export this file'), html, timer: null, showConfirmButton: true, showCancelButton: true, confirmButtonText: i18n.t('OK'), cancelButtonText: i18n.t('Report') });
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
openSendReportDialog(err);
|
openSendReportDialogWithState(err);
|
||||||
}
|
}
|
||||||
}, [openSendReportDialog]);
|
}, [openSendReportDialogWithState]);
|
||||||
|
|
||||||
const cutClick = useCallback(async () => {
|
const cutClick = useCallback(async () => {
|
||||||
if (working) return;
|
if (working) return;
|
||||||
|
@ -1519,7 +1487,7 @@ const App = memo(() => {
|
||||||
electron.ipcRenderer.on('openSettings', openSettings);
|
electron.ipcRenderer.on('openSettings', openSettings);
|
||||||
electron.ipcRenderer.on('openAbout', openAbout);
|
electron.ipcRenderer.on('openAbout', openAbout);
|
||||||
electron.ipcRenderer.on('batchConvertFriendlyFormat', batchConvertFriendlyFormat);
|
electron.ipcRenderer.on('batchConvertFriendlyFormat', batchConvertFriendlyFormat);
|
||||||
electron.ipcRenderer.on('openSendReportDialog', openSendReportDialog);
|
electron.ipcRenderer.on('openSendReportDialog', openSendReportDialogWithState);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
electron.ipcRenderer.removeListener('file-opened', fileOpened);
|
electron.ipcRenderer.removeListener('file-opened', fileOpened);
|
||||||
|
@ -1536,11 +1504,11 @@ const App = memo(() => {
|
||||||
electron.ipcRenderer.removeListener('openSettings', openSettings);
|
electron.ipcRenderer.removeListener('openSettings', openSettings);
|
||||||
electron.ipcRenderer.removeListener('openAbout', openAbout);
|
electron.ipcRenderer.removeListener('openAbout', openAbout);
|
||||||
electron.ipcRenderer.removeListener('batchConvertFriendlyFormat', batchConvertFriendlyFormat);
|
electron.ipcRenderer.removeListener('batchConvertFriendlyFormat', batchConvertFriendlyFormat);
|
||||||
electron.ipcRenderer.removeListener('openSendReportDialog', openSendReportDialog);
|
electron.ipcRenderer.removeListener('openSendReportDialog', openSendReportDialogWithState);
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
mergeFiles, outputDir, filePath, isFileOpened, customOutDir, startTimeOffset, html5ifyCurrentFile,
|
mergeFiles, outputDir, filePath, isFileOpened, customOutDir, startTimeOffset, html5ifyCurrentFile,
|
||||||
createDummyVideo, resetState, extractAllStreams, userOpenFiles, cutSegmentsHistory, openSendReportDialog,
|
createDummyVideo, resetState, extractAllStreams, userOpenFiles, cutSegmentsHistory, openSendReportDialogWithState,
|
||||||
loadEdlFile, cutSegments, edlFilePath, askBeforeClose, toggleHelp, toggleSettings, assureOutDirAccess, html5ifyAndLoad, html5ifyInternal,
|
loadEdlFile, cutSegments, edlFilePath, askBeforeClose, toggleHelp, toggleSettings, assureOutDirAccess, html5ifyAndLoad, html5ifyInternal,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -1673,6 +1641,8 @@ const App = memo(() => {
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// throw new Error('Test');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="no-user-select" style={{ background: controlsBackground, height: topBarHeight, display: 'flex', alignItems: 'center', padding: '0 5px', justifyContent: 'space-between', flexWrap: 'wrap' }}>
|
<div className="no-user-select" style={{ background: controlsBackground, height: topBarHeight, display: 'flex', alignItems: 'center', padding: '0 5px', justifyContent: 'space-between', flexWrap: 'wrap' }}>
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { openSendReportDialog } from './reporting';
|
||||||
|
|
||||||
|
class ErrorBoundary extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { error: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromError(error) {
|
||||||
|
return { error };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error, errorInfo) {
|
||||||
|
console.error('componentDidCatch', error, errorInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { error } = this.state;
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
||||||
|
<h1>Something went wrong</h1>
|
||||||
|
<div style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>{error.message}</div>
|
||||||
|
<p><button type="button" onClick={() => openSendReportDialog(error)} style={{ padding: 10, fontSize: 20 }}>Report error</button></p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ErrorBoundary;
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { Suspense } from 'react';
|
import React, { Suspense } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
import ErrorBoundary from './ErrorBoundary';
|
||||||
import './i18n';
|
import './i18n';
|
||||||
|
|
||||||
import './fonts.css';
|
import './fonts.css';
|
||||||
|
@ -11,4 +12,4 @@ const electron = window.require('electron');
|
||||||
|
|
||||||
console.log('Version', electron.remote.app.getVersion());
|
console.log('Version', electron.remote.app.getVersion());
|
||||||
|
|
||||||
ReactDOM.render(<Suspense fallback={<div />}><App /></Suspense>, document.getElementById('root'));
|
ReactDOM.render(<ErrorBoundary><Suspense fallback={<div />}><App /></Suspense></ErrorBoundary>, document.getElementById('root'));
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
import withReactContent from 'sweetalert2-react-content';
|
||||||
|
import i18n from 'i18next';
|
||||||
|
|
||||||
|
import { isStoreBuild } from './util';
|
||||||
|
|
||||||
|
const electron = window.require('electron'); // eslint-disable-line
|
||||||
|
|
||||||
|
|
||||||
|
const ReactSwal = withReactContent(Swal);
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export function openSendReportDialog(err, state) {
|
||||||
|
const reportInstructions = isStoreBuild
|
||||||
|
? <p>Please send an email to <span style={{ fontWeight: 'bold' }} role="button" onClick={() => electron.shell.openExternal('mailto:losslesscut@yankee.no')}>losslesscut@yankee.no</span> where you describe what you were doing.</p>
|
||||||
|
: <p>Please create an issue at <span style={{ fontWeight: 'bold' }} role="button" onClick={() => electron.shell.openExternal('https://github.com/mifi/lossless-cut/issues')}>https://github.com/mifi/lossless-cut/issues</span> where you describe what you were doing.</p>;
|
||||||
|
|
||||||
|
ReactSwal.fire({
|
||||||
|
showCloseButton: true,
|
||||||
|
title: i18n.t('Send problem report'),
|
||||||
|
html: (
|
||||||
|
<div style={{ textAlign: 'left', overflow: 'auto', maxHeight: 300, overflowY: 'auto' }}>
|
||||||
|
{reportInstructions}
|
||||||
|
|
||||||
|
<p>Include the following text:</p>
|
||||||
|
|
||||||
|
<div style={{ fontWeight: 600, fontSize: 12, whiteSpace: 'pre-wrap' }} contentEditable suppressContentEditableWarning>
|
||||||
|
{`${err ? err.stack : ''}\n\n${JSON.stringify({
|
||||||
|
err: err && {
|
||||||
|
code: err.code,
|
||||||
|
killed: err.killed,
|
||||||
|
failed: err.failed,
|
||||||
|
timedOut: err.timedOut,
|
||||||
|
isCanceled: err.isCanceled,
|
||||||
|
exitCode: err.exitCode,
|
||||||
|
signal: err.signal,
|
||||||
|
signalDescription: err.signalDescription,
|
||||||
|
},
|
||||||
|
|
||||||
|
state,
|
||||||
|
}, null, 2)}`}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
|
@ -187,3 +187,6 @@ export async function askForHtml5ifySpeed(allowedOptions) {
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isMasBuild = window.process.mas;
|
||||||
|
export const isStoreBuild = isMasBuild || window.process.windowsStore;
|
||||||
|
|
Ładowanie…
Reference in New Issue