1
Fork 0
mirror of https://github.com/Steffo99/sophon.git synced 2024-12-23 07:14:21 +00:00

Improve error handling

This commit is contained in:
Steffo 2021-10-15 18:07:45 +02:00 committed by Stefano Pigozzi
parent 695e1112c3
commit 4d9ea5cc8d
7 changed files with 180 additions and 113 deletions

View file

@ -37,68 +37,70 @@ import {ThemeProvider} from "./contexts/theme"
function App({..._}: RouteComponentProps) { function App({..._}: RouteComponentProps) {
return React.useMemo( return React.useMemo(
() => <> () => <>
<Chapter> <ErrorCatcherBox>
<SophonDescriptionBox/> <Chapter>
</Chapter> <SophonDescriptionBox/>
<InstanceProvider> </Chapter>
<InstanceRouter <InstanceProvider>
unselectedRoute={() => <> <InstanceRouter
<InstanceFormBox/> unselectedRoute={() => <>
</>} <InstanceFormBox/>
selectedRoute={() => <> </>}
<Chapter> selectedRoute={() => <>
<InstanceDescriptionBox/> <Chapter>
</Chapter> <InstanceDescriptionBox/>
<AuthorizationProvider> </Chapter>
<CacheProvider> <AuthorizationProvider>
<AuthorizationRouter <CacheProvider>
unselectedRoute={AuthorizationStepPage} <AuthorizationRouter
selectedRoute={() => <> unselectedRoute={AuthorizationStepPage}
<GroupRouter selectedRoute={() => <>
unselectedRoute={GroupStepPage} <GroupRouter
selectedRoute={({selection}) => <> unselectedRoute={GroupStepPage}
<GroupProvider resource={selection}> selectedRoute={({selection}) => <>
<Chapter> <GroupProvider resource={selection}>
<ResourceDescriptionBox resource={selection} icon={faUsers}/> <Chapter>
<GroupMembersBox/> <ResourceDescriptionBox resource={selection} icon={faUsers}/>
</Chapter> <GroupMembersBox/>
<ProjectRouter </Chapter>
groupPk={selection.value.slug} <ProjectRouter
unselectedRoute={({viewSet}) => <> groupPk={selection.value.slug}
<GroupCreateBox resource={selection}/> unselectedRoute={({viewSet}) => <>
<ProjectListBox viewSet={viewSet}/> <GroupCreateBox resource={selection}/>
<ProjectCreateBox viewSet={viewSet}/> <ProjectListBox viewSet={viewSet}/>
</>} <ProjectCreateBox viewSet={viewSet}/>
selectedRoute={({selection}) => <> </>}
<ProjectProvider resource={selection}> selectedRoute={({selection}) => <>
<ResourceDescriptionBox resource={selection} icon={faProjectDiagram}/> <ProjectProvider resource={selection}>
<NotebookRouter <ResourceDescriptionBox resource={selection} icon={faProjectDiagram}/>
projectPk={selection.value.slug} <NotebookRouter
unselectedRoute={({viewSet}) => <> projectPk={selection.value.slug}
<ProjectCreateBox resource={selection}/> unselectedRoute={({viewSet}) => <>
<NotebookListBox viewSet={viewSet}/> <ProjectCreateBox resource={selection}/>
<NotebookCreateBox viewSet={viewSet}/> <NotebookListBox viewSet={viewSet}/>
</>} <NotebookCreateBox viewSet={viewSet}/>
selectedRoute={({selection}) => <> </>}
<NotebookProvider resource={selection}> selectedRoute={({selection}) => <>
<NotebookDescriptionBox/> <NotebookProvider resource={selection}>
<NotebookCreateBox resource={selection}/> <NotebookDescriptionBox/>
</NotebookProvider> <NotebookCreateBox resource={selection}/>
</>} </NotebookProvider>
/> </>}
</ProjectProvider> />
</>} </ProjectProvider>
/> </>}
</GroupProvider> />
</>} </GroupProvider>
/> </>}
</>} />
/> </>}
</CacheProvider> />
</AuthorizationProvider> </CacheProvider>
</>} </AuthorizationProvider>
/> </>}
</InstanceProvider> />
</InstanceProvider>
</ErrorCatcherBox>
</>, </>,
[], [],
) )
@ -112,11 +114,9 @@ export default function AppWrapper() {
<LayoutThreeCol> <LayoutThreeCol>
<LayoutThreeCol.Center> <LayoutThreeCol.Center>
<ThemedTitle level={1}/> <ThemedTitle level={1}/>
<ErrorCatcherBox>
<Reach.Router> <Reach.Router>
<App default/> <App default/>
</Reach.Router> </Reach.Router>
</ErrorCatcherBox>
<SophonFooter/> <SophonFooter/>
</LayoutThreeCol.Center> </LayoutThreeCol.Center>
</LayoutThreeCol> </LayoutThreeCol>

View file

@ -0,0 +1,34 @@
import {faLevelUpAlt} from "@fortawesome/free-solid-svg-icons"
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
import * as Reach from "@reach/router"
import {Button} from "@steffo/bluelib-react"
import {ButtonProps} from "@steffo/bluelib-react/dist/components/inputs/Button"
import * as React from "react"
import {useSophonPath} from "../../hooks/useSophonPath"
/**
* A button that takes the user back one sophon level (so two levels).
*
* @constructor
*/
export function GoBackButton({onClick, ...props}: Omit<ButtonProps, "children">): JSX.Element {
const location = useSophonPath()
const onClickWrapped = React.useCallback(
async event => {
event.preventDefault()
if(onClick) {
onClick(event)
}
await Reach.navigate("../..")
},
[onClick],
)
return (
<Button onClick={onClickWrapped} disabled={location.count === 0} {...props}>
<FontAwesomeIcon icon={faLevelUpAlt}/>&nbsp;Go up
</Button>
)
}

View file

@ -0,0 +1,40 @@
import * as Reach from "@reach/router"
import {Button} from "@steffo/bluelib-react"
import {ButtonProps} from "@steffo/bluelib-react/dist/components/inputs/Button"
import * as React from "react"
/**
* The props of {@link NavigateButton}.
*/
export interface NavigateButtonProps extends ButtonProps {
href: string,
}
/**
* A button functioning like a {@link Link}.
*
* @constructor
*/
export function NavigateButton({href, children, onClick, ...props}: NavigateButtonProps): JSX.Element {
const location = Reach.useLocation()
const onClickWrapped = React.useCallback(
event => {
event.preventDefault()
if(onClick) {
onClick(event)
}
if(href) {
// noinspection JSIgnoredPromiseFromCall
Reach.navigate(href)
}
},
[href, onClick],
)
return (
<Button children={children} onClick={onClickWrapped} disabled={location.pathname === href} {...props}/>
)
}

View file

@ -1,7 +1,9 @@
import {Box, Form} from "@steffo/bluelib-react" import {faAngleDoubleRight, faBug} from "@fortawesome/free-solid-svg-icons"
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
import {Box, Button, Form} from "@steffo/bluelib-react"
import * as React from "react" import * as React from "react"
import {IgnoreErrorButton} from "./IgnoreErrorButton" import {GoBackButton} from "../elements/GoBackButton"
import {ReportBugButton} from "./ReportBugButton" import {NavigateButton} from "../elements/NavigateButton"
interface ErrorCatcherBoxProps { interface ErrorCatcherBoxProps {
@ -44,8 +46,13 @@ export class ErrorCatcherBox extends React.Component<ErrorCatcherBoxProps, Error
{this.state.error.toString()} {this.state.error.toString()}
<Form> <Form>
<Form.Row> <Form.Row>
<IgnoreErrorButton onClick={this.clearError}/> <NavigateButton href={"https://github.com/Steffo99/sophon/issues/new?assignees=&labels=bug&template=1_bug_report.md&title="}>
<ReportBugButton/> <FontAwesomeIcon icon={faBug}/>&nbsp;Report bug
</NavigateButton>
<GoBackButton/>
<Button onClick={this.clearError}>
<FontAwesomeIcon icon={faAngleDoubleRight}/>&nbsp;Try ignoring the error
</Button>
</Form.Row> </Form.Row>
</Form> </Form>
</Box> </Box>

View file

@ -1,18 +0,0 @@
import {faAngleDoubleRight} from "@fortawesome/free-solid-svg-icons"
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
import {Button} from "@steffo/bluelib-react"
import * as React from "react"
export interface IgnoreErrorButtonProps {
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
}
export function IgnoreErrorButton({onClick}: IgnoreErrorButtonProps): JSX.Element {
return (
<Button onClick={onClick}>
<FontAwesomeIcon icon={faAngleDoubleRight}/>&nbsp;Try ignoring the error
</Button>
)
}

View file

@ -1,25 +0,0 @@
import {faBug} from "@fortawesome/free-solid-svg-icons"
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
import {navigate} from "@reach/router"
import {Button} from "@steffo/bluelib-react"
import * as React from "react"
export function ReportBugButton(): JSX.Element {
const onClick =
React.useCallback(
async () => {
await navigate("https://github.com/Steffo99/sophon/issues/new?assignees=&labels=bug&template=1_bug_report.md&title=")
},
[],
)
return React.useMemo(
() => (
<Button>
<FontAwesomeIcon onClick={onClick} icon={faBug}/>&nbsp;Report bug
</Button>
),
[onClick],
)
}

View file

@ -37,6 +37,11 @@ export interface ParsedPath {
* Passed the login page (either by browsing as guest or by logging in). * Passed the login page (either by browsing as guest or by logging in).
*/ */
loggedIn?: boolean, loggedIn?: boolean,
/**
* The number of pages that separate this to the website root.
*/
count: number,
} }
/** /**
@ -44,7 +49,9 @@ export interface ParsedPath {
* @param path - The path to split. * @param path - The path to split.
*/ */
export function parsePath(path: string): ParsedPath { export function parsePath(path: string): ParsedPath {
let result: ParsedPath = {} let result: ParsedPath = {
count: 0,
}
result.instance = path.match(/[/]i[/]([^/]+)/)?.[1] result.instance = path.match(/[/]i[/]([^/]+)/)?.[1]
result.userId = path.match(/[/]u[/]([0-9]+)/)?.[1] result.userId = path.match(/[/]u[/]([0-9]+)/)?.[1]
@ -54,5 +61,27 @@ export function parsePath(path: string): ParsedPath {
result.notebook = path.match(/[/]n[/]([A-Za-z0-9_-]+)/)?.[1] result.notebook = path.match(/[/]n[/]([A-Za-z0-9_-]+)/)?.[1]
result.loggedIn = Boolean(path.match(/[/]l[/]/)) result.loggedIn = Boolean(path.match(/[/]l[/]/))
if(result.instance) {
result.count += 1
}
if(result.userId) {
result.count += 1
}
if(result.userName) {
result.count += 1
}
if(result.researchGroup) {
result.count += 1
}
if(result.researchProject) {
result.count += 1
}
if(result.notebook) {
result.count += 1
}
if(result.loggedIn) {
result.count += 1
}
return result return result
} }