Skip to content

Commit

Permalink
πŸ› Allow signing back in after client-side exception
Browse files Browse the repository at this point in the history
This at least allow users to recover from some bugs until they're fixed
  • Loading branch information
homostellaris committed Aug 11, 2024
1 parent 1f7021d commit 7ddf7e5
Show file tree
Hide file tree
Showing 17 changed files with 226 additions and 167 deletions.
4 changes: 2 additions & 2 deletions app/[...all]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import dynamic from 'next/dynamic'

// TODO: Find out if this is necessary to be compatible with Ionic & Capacitor
const App = dynamic(() => import('../../components/AppShell'), {
const LazyApp = dynamic(() => import('../../components/App'), {
ssr: false,
})

Expand All @@ -10,5 +10,5 @@ export async function generateStaticParams() {
}

export default function Page() {
return <App />
return <LazyApp />
}
23 changes: 13 additions & 10 deletions components/AppShell.tsx β†’ components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { IonReactRouter } from '@ionic/react-router'
import { Route } from 'react-router-dom'
import Home from './pages/Home'
import Constellation from './pages/Constellation'
import ErrorBoundary from './ErrorBoundary'

setupIonicReact({})

Expand All @@ -18,23 +19,25 @@ window
} catch {}
})

const AppShell = () => {
const App = () => {
return (
<IonApp>
<IonReactRouter>
<IonRouterOutlet id="main">
<Route
path="/constellation"
render={() => <Constellation />}
/>
<Route
path="/home"
render={() => <Home />}
/>
<ErrorBoundary>
<Route
path="/constellation"
render={() => <Constellation />}
/>
<Route
path="/home"
render={() => <Home />}
/>
</ErrorBoundary>
</IonRouterOutlet>
</IonReactRouter>
</IonApp>
)
}

export default AppShell
export default App
42 changes: 42 additions & 0 deletions components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Component, ErrorInfo, ReactNode } from 'react'
import { Header } from './common/Header'

interface Props {
children?: ReactNode
}

interface State {
hasError: boolean
}

class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
}

public static getDerivedStateFromError(_: Error): State {
return { hasError: true }
}

public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Uncaught error:', error, errorInfo)
}

public render() {
if (this.state.hasError) {
return (
<div>
<Header title="Error" />
<p className="mx-auto my-4 text-xl text-center max-w-prose">
Sorry there was an error. If you&apos;ve unsynced, re-syncing might
fix the issue.
</p>
</div>
)
}

return this.props.children
}
}

export default ErrorBoundary
156 changes: 156 additions & 0 deletions components/common/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { useObservable } from 'dexie-react-hooks'
import { db } from '../db'
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonImg,
IonInput,
IonItem,
IonList,
IonPopover,
IonTitle,
IonToolbar,
} from '@ionic/react'
import {
cloudDoneSharp,
cloudDownloadSharp,
cloudOfflineSharp,
cloudUploadSharp,
thunderstormSharp,
} from 'ionicons/icons'

export const Header = ({ title }: { title: string }) => {
const user = useObservable(db.cloud.currentUser)
const syncState = useObservable(db.cloud.syncState)
const isLoggedIn = user?.isLoggedIn

return (
<IonHeader>
<IonToolbar>
<IonImg
src="/logo.png"
slot="start"
className="w-10 h-10 ml-4"
/>
<IonTitle>{title}</IonTitle>
<IonButtons
className="mx-2"
slot="secondary"
>
{isLoggedIn ? (
<>
<IonButton id="sync-status">
<IonIcon
icon={
syncState?.error
? thunderstormSharp
: syncState?.phase === 'pushing'
? cloudUploadSharp
: syncState?.phase === 'pulling'
? cloudDownloadSharp
: cloudDoneSharp
}
color={syncState?.error ? 'danger' : 'default'}
slot="icon-only"
></IonIcon>
<IonPopover
trigger="sync-status"
triggerAction="click"
>
<IonContent className="text-xs">
{syncState?.error ? (
<p>Sync error: ${syncState.error.message}</p>
) : (
<IonList>
<IonItem>
<IonInput
label="Email"
labelPlacement="floating"
readonly
value={user.email}
></IonInput>
</IonItem>

<IonItem>
<IonInput
label="License"
labelPlacement="floating"
readonly
value={syncState?.license}
></IonInput>
</IonItem>

<IonItem>
<IonInput
label="Status"
labelPlacement="floating"
readonly
value={syncState?.status}
></IonInput>
</IonItem>

<IonItem>
<IonInput
label="Phase"
labelPlacement="floating"
readonly
value={syncState?.phase}
></IonInput>
</IonItem>

<IonItem>
<IonInput
label="Progress"
labelPlacement="floating"
readonly
value={syncState?.progress || '-'}
></IonInput>
</IonItem>
</IonList>
)}
</IonContent>
</IonPopover>
</IonButton>
<IonButton
fill="solid"
onClick={() => {
db.cloud.logout()
}}
>
Unsync
</IonButton>
</>
) : (
<>
<IonButton id="sync-status">
<IonIcon
icon={cloudOfflineSharp}
slot="icon-only"
></IonIcon>
<IonPopover
trigger="sync-status"
triggerAction="click"
>
<IonContent class="ion-padding">
Not synced. Your data is stored locally only.
</IonContent>
</IonPopover>
</IonButton>
<IonButton
fill="solid"
onClick={() => {
db.cloud.login()
}}
>
Sync
</IonButton>
</>
)}
</IonButtons>
</IonToolbar>
</IonHeader>
)
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
12 changes: 6 additions & 6 deletions components/app/index.tsx β†’ components/demo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useEffect, useState } from 'react'
import ControlPanel from '../app/ControlPanel'
import Journey from '../app/Journey'
import PerformanceSidebar from '../app/PerformanceSidebar'
import RoleSidebar from '../app/RoleSidebar'
import ControlPanel from './ControlPanel'
import Journey from './Journey'
import PerformanceSidebar from './PerformanceSidebar'
import RoleSidebar from './RoleSidebar'
import Planets from '../common/Planets'
import Events from '../todos/Events'
import useTodos from '../todos/useTodos'
Expand Down Expand Up @@ -45,10 +45,10 @@ export default function App() {
expanded={eventsExpanded}
/>
</header>
<div className="left-column fixed left-0 top-24 z-10 hidden w-1/6 p-4 text-white lg:block">
<div className="fixed left-0 z-10 hidden w-1/6 p-4 text-white left-column top-24 lg:block">
<PerformanceSidebar />
</div>
<div className="right-column fixed right-0 top-24 z-10 hidden w-1/6 p-4 lg:block">
<div className="fixed right-0 z-10 hidden w-1/6 p-4 right-column top-24 lg:block">
<RoleSidebar />
</div>
<div className="parallax-container h-full scroll-p-[calc(100vh-10rem)]">
Expand Down
File renamed without changes
File renamed without changes
7 changes: 2 additions & 5 deletions components/pages/Constellation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { db } from '../db'
import { useCreateStarRoleModal } from '../starRoles/create/useCreateStarRoleModal'
import { getIonIcon } from '../starRoles/icons'
import { useStarRoleActionSheet } from '../starRoles/StarRoleActionSheet'
import { Header } from '../common/Header'

export default function Constellation() {
const starRoles = useLiveQuery(() => db.starRoles.toArray())
Expand All @@ -42,11 +43,7 @@ export default function Constellation() {

return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Constellation</IonTitle>
</IonToolbar>
</IonHeader>
<Header title="Constellation" />
<IonContent fullscreen>
{isLoading ? (
<div className="flex items-center justify-center h-full">
Expand Down
Loading

0 comments on commit 7ddf7e5

Please sign in to comment.