Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
App core tech debt componentization (#10395)
Browse files Browse the repository at this point in the history
* convert webapp injection, auth, notification to hooks/components and optimize engine loader

* license

* cleanup

* refactor tw theme provider into hook

* cleanup

* update loading circles to loading views

---------

Co-authored-by: Appaji <[email protected]>
  • Loading branch information
HexaField and CITIZENDOT authored Jun 17, 2024
1 parent 282861d commit ee03b64
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import { SnackbarProvider, VariantType } from 'notistack'
import { SnackbarKey, SnackbarProvider, VariantType, closeSnackbar } from 'notistack'
import React, { CSSProperties, Fragment, useEffect, useRef } from 'react'

import multiLogger from '@etherealengine/common/src/logger'
import { AudioEffectPlayer } from '@etherealengine/engine/src/audio/systems/MediaSystem'
import { defineState, getState } from '@etherealengine/hyperflux'
import { defineState, getState, useMutableState } from '@etherealengine/hyperflux'

import { defaultAction } from '../components/NotificationActions'
import Icon from '@etherealengine/ui/src/primitives/mui/Icon'
import IconButton from '@etherealengine/ui/src/primitives/mui/IconButton'

const logger = multiLogger.child({ component: 'client-core:Notification' })

Expand All @@ -45,6 +47,15 @@ export type NotificationOptions = {
actionType?: keyof typeof NotificationActions
}

export const defaultAction = (key: SnackbarKey, content?: React.ReactNode) => {
return (
<Fragment>
{content}
<IconButton onClick={() => closeSnackbar(key)} icon={<Icon type="Close" sx={{ color: 'white' }} />} />
</Fragment>
)
}

export const NotificationActions = {
default: defaultAction
}
Expand All @@ -63,3 +74,22 @@ export const NotificationService = {
})
}
}

export const NotificationSnackbar = (props: { style?: CSSProperties }) => {
const notistackRef = useRef<SnackbarProvider>()
const notificationstate = useMutableState(NotificationState)

useEffect(() => {
notificationstate.snackbar.set(notistackRef.current)
}, [notistackRef.current])

return (
<SnackbarProvider
ref={notistackRef as any}
maxSnack={7}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
action={defaultAction}
style={props.style}
/>
)
}
6 changes: 2 additions & 4 deletions packages/client-core/src/common/services/ThemeService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import React, { useEffect } from 'react'
import { useEffect } from 'react'

import { defineState, getMutableState, syncStateWithLocalStorage, useMutableState } from '@etherealengine/hyperflux'

Expand Down Expand Up @@ -96,7 +96,7 @@ export const ThemeState = defineState({
extension: syncStateWithLocalStorage(['theme'])
})

export const ThemeProvider = ({ children }) => {
export const useThemeProvider = () => {
const themeState = useMutableState(ThemeState)

useEffect(() => {
Expand All @@ -120,6 +120,4 @@ export const ThemeProvider = ({ children }) => {
}
}
}

return <>{children}</>
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,32 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import { SnackbarKey, useSnackbar } from 'notistack'
import React, { Fragment } from 'react'
import { NO_PROXY } from '@etherealengine/hyperflux'
import { loadWebappInjection } from '@etherealengine/projects/loadWebappInjection'
import LoadingView from '@etherealengine/ui/src/primitives/tailwind/LoadingView'
import { useHookstate } from '@hookstate/core'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'

import Icon from '@etherealengine/ui/src/primitives/mui/Icon'
import IconButton from '@etherealengine/ui/src/primitives/mui/IconButton'
export const LoadWebappInjection = (props) => {
const { t } = useTranslation()

export const defaultAction = (key: SnackbarKey, content?: React.ReactNode) => {
const { closeSnackbar } = useSnackbar()
const projectComponents = useHookstate(null as null | any[])

useEffect(() => {
loadWebappInjection().then((result) => {
projectComponents.set(result)
})
}, [])

if (!projectComponents.value) return <LoadingView title={t('common:loader.authenticating')} />

return (
<Fragment>
{content}
<IconButton onClick={() => closeSnackbar(key)} icon={<Icon type="Close" sx={{ color: 'white' }} />} />
</Fragment>
<>
{projectComponents.get(NO_PROXY)!.map((Component, i) => (
<Component key={i} />
))}
{props.children}
</>
)
}
17 changes: 16 additions & 1 deletion packages/client-core/src/user/services/AuthService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ import {
dispatchAction,
getMutableState,
getState,
syncStateWithLocalStorage
syncStateWithLocalStorage,
useHookstate
} from '@etherealengine/hyperflux'

import { API } from '../../API'
Expand Down Expand Up @@ -759,3 +760,17 @@ function parseLoginDisplayCredential(credentials) {

return { displayName, displayIcon }
}

export const useAuthenticated = () => {
const authState = useHookstate(getMutableState(AuthState))

useEffect(() => {
AuthService.doLoginAuto()
}, [])

useEffect(() => {
Engine.instance.userID = authState.user.id.value
}, [authState.user.id])

return authState.isLoggedIn.value
}
27 changes: 19 additions & 8 deletions packages/client/src/engine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import React, { createRef, Suspense } from 'react'
import React, { Suspense, useEffect } from 'react'
import { useTranslation } from 'react-i18next'

import { API } from '@etherealengine/client-core/src/API'
import { LoadingCircle } from '@etherealengine/client-core/src/components/LoadingCircle'
import { BrowserRouter, history } from '@etherealengine/client-core/src/common/services/RouterService'
import waitForClientAuthenticated from '@etherealengine/client-core/src/util/wait-for-client-authenticated'
import { pipeLogs } from '@etherealengine/common/src/logger'
import { Engine } from '@etherealengine/ecs/src/Engine'
Expand All @@ -36,6 +36,7 @@ import { getMutableState } from '@etherealengine/hyperflux'
import { EngineState } from '@etherealengine/spatial/src/EngineState'
import { createEngine } from '@etherealengine/spatial/src/initializeEngine'

import LoadingView from '@etherealengine/ui/src/primitives/tailwind/LoadingView'
import { initializei18n } from './util'

const initializeLogs = async () => {
Expand All @@ -53,12 +54,22 @@ initializeBrowser()
API.createAPI()
initializeLogs()

export default function ({ children, tailwind = false }): JSX.Element {
const ref = createRef()
export default function ({ children }): JSX.Element {
const { t } = useTranslation()
return !tailwind ? (
<Suspense fallback={<LoadingCircle message={t('common:loader.loadingClient')} />}>{children}</Suspense>
) : (
children

useEffect(() => {
const urlSearchParams = new URLSearchParams(window.location.search)
const redirectUrl = urlSearchParams.get('redirectUrl')
if (redirectUrl) {
history.push(redirectUrl)
}
}, [])

return (
<>
<BrowserRouter history={history}>
<Suspense fallback={<LoadingView title={t('common:loader.loadingClient')} />}>{children}</Suspense>
</BrowserRouter>
</>
)
}
27 changes: 6 additions & 21 deletions packages/client/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ Ethereal Engine. All Rights Reserved.
*/

import { t } from 'i18next'
import React, { lazy, Suspense, useEffect } from 'react'
import React, { lazy, Suspense } from 'react'
import { createRoot } from 'react-dom/client'
import { Route, Routes } from 'react-router-dom'

import ErrorBoundary from '@etherealengine/client-core/src/common/components/ErrorBoundary'
import { BrowserRouter, history } from '@etherealengine/client-core/src/common/services/RouterService'
import { LoadingCircle } from '@etherealengine/client-core/src/components/LoadingCircle'

import './pages/styles.scss'
Expand All @@ -45,27 +44,17 @@ const AppPage = lazy(() => import('./pages/_app'))
const TailwindPage = lazy(() => import('./pages/_app_tw'))

const App = () => {
useEffect(() => {
const urlSearchParams = new URLSearchParams(window.location.search)
const redirectUrl = urlSearchParams.get('redirectUrl')
if (redirectUrl) {
history.push(redirectUrl)
}
}, [])

return (
<ErrorBoundary>
<BrowserRouter history={history}>
<Engine>
<Routes>
{/* @todo - these are for backwards compatibility with non tailwind pages - they will be removed eventually */}
<Route
key="location"
path="/location/*"
element={
<Suspense fallback={<LoadingCircle message={t('common:loader.starting')} />}>
<Engine>
<AppPage route={'location'} />
</Engine>
<AppPage route={'location'} />
</Suspense>
}
/>
Expand All @@ -74,9 +63,7 @@ const App = () => {
path="/offline/*"
element={
<Suspense fallback={<LoadingCircle message={t('common:loader.starting')} />}>
<Engine>
<AppPage route={'offline'} />
</Engine>
<AppPage route={'offline'} />
</Suspense>
}
/>
Expand All @@ -86,14 +73,12 @@ const App = () => {
path="/*"
element={
<Suspense>
<Engine tailwind>
<TailwindPage />
</Engine>
<TailwindPage />
</Suspense>
}
/>
</Routes>
</BrowserRouter>
</Engine>
</ErrorBoundary>
)
}
Expand Down
59 changes: 15 additions & 44 deletions packages/client/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,22 @@ Ethereal Engine. All Rights Reserved.
*/

// import * as chapiWalletPolyfill from 'credential-handler-polyfill'
import { SnackbarProvider } from 'notistack'
import React, { useEffect, useRef, useState } from 'react'
import React, { useEffect } from 'react'

import { initGA, logPageView } from '@etherealengine/client-core/src/common/analytics'
import { defaultAction } from '@etherealengine/client-core/src/common/components/NotificationActions'
import { NotificationState } from '@etherealengine/client-core/src/common/services/NotificationService'
import { NotificationSnackbar } from '@etherealengine/client-core/src/common/services/NotificationService'
import Debug from '@etherealengine/client-core/src/components/Debug'
import InviteToast from '@etherealengine/client-core/src/components/InviteToast'
import { AuthService, AuthState } from '@etherealengine/client-core/src/user/services/AuthService'
import { useAuthenticated } from '@etherealengine/client-core/src/user/services/AuthService'

import '@etherealengine/client-core/src/util/GlobalStyle.css'

import { StyledEngineProvider, Theme } from '@mui/material/styles'
import { useTranslation } from 'react-i18next'

import { LoadingCircle } from '@etherealengine/client-core/src/components/LoadingCircle'
import { Engine } from '@etherealengine/ecs/src/Engine'
import { useMutableState } from '@etherealengine/hyperflux'
import { loadWebappInjection } from '@etherealengine/projects/loadWebappInjection'

import { LoadWebappInjection } from '@etherealengine/client-core/src/components/LoadWebappInjection'
import RouterComp from '../route/public'
import { ThemeContextProvider } from './themeContext'

Expand All @@ -54,60 +50,35 @@ declare module '@mui/styles/defaultTheme' {

/** @deprecated see https://github.com/EtherealEngine/etherealengine/issues/6485 */
const AppPage = ({ route }: { route: string }) => {
const notistackRef = useRef<SnackbarProvider>()
const authState = useMutableState(AuthState)
const isLoggedIn = useMutableState(AuthState).isLoggedIn
const selfUser = authState.user
const [projectComponents, setProjectComponents] = useState<Array<any> | null>(null)
const notificationstate = useMutableState(NotificationState)
const isLoggedIn = useAuthenticated()
const { t } = useTranslation()

useEffect(() => {
AuthService.doLoginAuto()
initGA()
logPageView()
}, [])

useEffect(() => {
notificationstate.snackbar.set(notistackRef.current)
}, [notistackRef.current])

useEffect(() => {
if (!isLoggedIn.value || projectComponents) return
loadWebappInjection().then((result) => {
setProjectComponents(result)
})
}, [isLoggedIn])

useEffect(() => {
Engine.instance.userID = selfUser.id.value
}, [selfUser.id])

if (!isLoggedIn.value) {
if (!isLoggedIn) {
return <LoadingCircle message={t('common:loader.authenticating')} />
}

return (
<>
<ThemeContextProvider>
<StyledEngineProvider injectFirst>
<SnackbarProvider
ref={notistackRef as any}
maxSnack={7}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
action={defaultAction}
<NotificationSnackbar
style={{
fontFamily: 'var(--lato)',
fontSize: '12px'
}}
>
<div style={{ pointerEvents: 'auto' }}>
<InviteToast />
<Debug />
</div>
{projectComponents && <RouterComp route={route} />}
{projectComponents?.map((Component, i) => <Component key={i} />)}
</SnackbarProvider>
/>
<div style={{ pointerEvents: 'auto' }}>
<InviteToast />
<Debug />
</div>
<LoadWebappInjection>
<RouterComp route={route} />
</LoadWebappInjection>
</StyledEngineProvider>
</ThemeContextProvider>
</>
Expand Down
Loading

0 comments on commit ee03b64

Please sign in to comment.