Skip to content

Commit

Permalink
Merge pull request janhq#4146 from janhq/fix/app-rerender-issues
Browse files Browse the repository at this point in the history
fix: app re-render issues caused by bad state handling
  • Loading branch information
louis-jan authored Nov 27, 2024
2 parents eb3669e + 3a68f29 commit cd1a274
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 132 deletions.
27 changes: 4 additions & 23 deletions web/containers/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
'use client'

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

import { motion as m } from 'framer-motion'

import { useAtom, useAtomValue } from 'jotai'
import { useAtomValue, useSetAtom } from 'jotai'

import { twMerge } from 'tailwind-merge'

Expand Down Expand Up @@ -36,7 +34,7 @@ import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'

const BaseLayout = () => {
const [mainViewState, setMainViewState] = useAtom(mainViewStateAtom)
const setMainViewState = useSetAtom(mainViewStateAtom)
const importModelStage = useAtomValue(getImportModelStageAtom)
const reduceTransparent = useAtomValue(reduceTransparentAtom)

Expand Down Expand Up @@ -68,24 +66,7 @@ const BaseLayout = () => {
<TopPanel />
<div className="relative top-9 flex h-[calc(100vh-(36px+36px))] w-screen">
<RibbonPanel />
<div className={twMerge('relative flex w-full')}>
<div className="w-full">
<m.div
key={mainViewState}
initial={{ opacity: 0, y: -8 }}
className="h-full"
animate={{
opacity: 1,
y: 0,
transition: {
duration: 0.5,
},
}}
>
<MainViewContainer />
</m.div>
</div>
</div>
<MainViewContainer />
<LoadingModal />
{importModelStage === 'SELECTING_MODEL' && <SelectingModelModal />}
{importModelStage === 'MODEL_SELECTED' && <ImportModelOptionModal />}
Expand Down
28 changes: 26 additions & 2 deletions web/containers/MainViewContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { memo } from 'react'

import { motion as m } from 'framer-motion'
import { useAtomValue } from 'jotai'

import { twMerge } from 'tailwind-merge'

import { MainViewState } from '@/constants/screens'

import HubScreen from '@/screens/Hub'
Expand Down Expand Up @@ -31,7 +36,26 @@ const MainViewContainer = () => {
break
}

return children
return (
<div className={twMerge('relative flex w-full')}>
<div className="w-full">
<m.div
key={mainViewState}
initial={{ opacity: 0, y: -8 }}
className="h-full"
animate={{
opacity: 1,
y: 0,
transition: {
duration: 0.25,
},
}}
>
{children}
</m.div>
</div>
</div>
)
}

export default MainViewContainer
export default memo(MainViewContainer)
64 changes: 64 additions & 0 deletions web/containers/Providers/CoreConfigurator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use client'

import { PropsWithChildren, useCallback, useEffect, useState } from 'react'

import Loader from '@/containers/Loader'

import { setupCoreServices } from '@/services/coreService'
import {
isCoreExtensionInstalled,
setupBaseExtensions,
} from '@/services/extensionService'

import { extensionManager } from '@/extension'

export const CoreConfigurator = ({ children }: PropsWithChildren) => {
const [setupCore, setSetupCore] = useState(false)
const [activated, setActivated] = useState(false)
const [settingUp, setSettingUp] = useState(false)

const setupExtensions = useCallback(async () => {
// Register all active extensions
await extensionManager.registerActive()

setTimeout(async () => {
if (!isCoreExtensionInstalled()) {
setSettingUp(true)
await setupBaseExtensions()
return
}

extensionManager.load()
setSettingUp(false)
setActivated(true)
}, 500)
}, [])

// Services Setup
useEffect(() => {
setupCoreServices()
setSetupCore(true)
return () => {
extensionManager.unload()
}
}, [])

useEffect(() => {
if (setupCore) {
// Electron
if (window && window.core?.api) {
setupExtensions()
} else {
// Host
setActivated(true)
}
}
}, [setupCore, setupExtensions])

return (
<>
{settingUp && <Loader description="Preparing Update..." />}
{setupCore && activated && <>{children}</>}
</>
)
}
59 changes: 4 additions & 55 deletions web/containers/Providers/index.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
'use client'

import { PropsWithChildren, useCallback, useEffect, useState } from 'react'
import { PropsWithChildren } from 'react'

import { Toaster } from 'react-hot-toast'

import Loader from '@/containers/Loader'
import EventListener from '@/containers/Providers/EventListener'
import JotaiWrapper from '@/containers/Providers/Jotai'

import ThemeWrapper from '@/containers/Providers/Theme'

import { setupCoreServices } from '@/services/coreService'
import {
isCoreExtensionInstalled,
setupBaseExtensions,
} from '@/services/extensionService'

import Umami from '@/utils/umami'

import { CoreConfigurator } from './CoreConfigurator'
import DataLoader from './DataLoader'

import DeepLinkListener from './DeepLinkListener'
Expand All @@ -26,57 +20,12 @@ import Responsive from './Responsive'

import SettingsHandler from './SettingsHandler'

import { extensionManager } from '@/extension'

const Providers = ({ children }: PropsWithChildren) => {
const [setupCore, setSetupCore] = useState(false)
const [activated, setActivated] = useState(false)
const [settingUp, setSettingUp] = useState(false)

const setupExtensions = useCallback(async () => {
// Register all active extensions
await extensionManager.registerActive()

setTimeout(async () => {
if (!isCoreExtensionInstalled()) {
setSettingUp(true)
await setupBaseExtensions()
return
}

extensionManager.load()
setSettingUp(false)
setActivated(true)
}, 500)
}, [])

// Services Setup
useEffect(() => {
setupCoreServices()
setSetupCore(true)
return () => {
extensionManager.unload()
}
}, [])

useEffect(() => {
if (setupCore) {
// Electron
if (window && window.core?.api) {
setupExtensions()
} else {
// Host
setActivated(true)
}
}
}, [setupCore, setupExtensions])

return (
<ThemeWrapper>
<JotaiWrapper>
<Umami />
{settingUp && <Loader description="Preparing Update..." />}
{setupCore && activated && (
<CoreConfigurator>
<>
<Responsive />
<KeyListener />
Expand All @@ -87,7 +36,7 @@ const Providers = ({ children }: PropsWithChildren) => {
<Toaster />
{children}
</>
)}
</CoreConfigurator>
</JotaiWrapper>
</ThemeWrapper>
)
Expand Down
33 changes: 27 additions & 6 deletions web/helpers/atoms/AppConfig.atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,35 @@ export const janDataFolderPathAtom = atom('')

export const experimentalFeatureEnabledAtom = atomWithStorage(
EXPERIMENTAL_FEATURE,
false
false,
undefined,
{ getOnInit: true }
)

export const proxyEnabledAtom = atomWithStorage(PROXY_FEATURE_ENABLED, false)
export const proxyAtom = atomWithStorage(HTTPS_PROXY_FEATURE, '')
export const proxyEnabledAtom = atomWithStorage(
PROXY_FEATURE_ENABLED,
false,
undefined,
{ getOnInit: true }
)
export const proxyAtom = atomWithStorage(HTTPS_PROXY_FEATURE, '', undefined, {
getOnInit: true,
})

export const ignoreSslAtom = atomWithStorage(IGNORE_SSL, false)
export const vulkanEnabledAtom = atomWithStorage(VULKAN_ENABLED, false)
export const quickAskEnabledAtom = atomWithStorage(QUICK_ASK_ENABLED, false)
export const ignoreSslAtom = atomWithStorage(IGNORE_SSL, false, undefined, {
getOnInit: true,
})
export const vulkanEnabledAtom = atomWithStorage(
VULKAN_ENABLED,
false,
undefined,
{ getOnInit: true }
)
export const quickAskEnabledAtom = atomWithStorage(
QUICK_ASK_ENABLED,
false,
undefined,
{ getOnInit: true }
)

export const hostAtom = atom('http://localhost:1337/')
8 changes: 6 additions & 2 deletions web/helpers/atoms/Model.atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ enum ModelStorageAtomKeys {
*/
export const downloadedModelsAtom = atomWithStorage<Model[]>(
ModelStorageAtomKeys.DownloadedModels,
[]
[],
undefined,
{ getOnInit: true }
)

/**
Expand All @@ -25,7 +27,9 @@ export const downloadedModelsAtom = atomWithStorage<Model[]>(
*/
export const configuredModelsAtom = atomWithStorage<Model[]>(
ModelStorageAtomKeys.AvailableModels,
[]
[],
undefined,
{ getOnInit: true }
)

export const removeDownloadedModelAtom = atom(
Expand Down
18 changes: 15 additions & 3 deletions web/helpers/atoms/Setting.atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,22 @@ export const REDUCE_TRANSPARENT = 'reduceTransparent'
export const SPELL_CHECKING = 'spellChecking'
export const themesOptionsAtom = atom<{ name: string; value: string }[]>([])
export const janThemesPathAtom = atom<string | undefined>(undefined)
export const selectedThemeIdAtom = atomWithStorage<string>(THEME, '')
export const selectedThemeIdAtom = atomWithStorage<string>(
THEME,
'',
undefined,
{ getOnInit: true }
)
export const themeDataAtom = atom<Theme | undefined>(undefined)
export const reduceTransparentAtom = atomWithStorage<boolean>(
REDUCE_TRANSPARENT,
false
false,
undefined,
{ getOnInit: true }
)
export const spellCheckAtom = atomWithStorage<boolean>(
SPELL_CHECKING,
false,
undefined,
{ getOnInit: true }
)
export const spellCheckAtom = atomWithStorage<boolean>(SPELL_CHECKING, false)
4 changes: 3 additions & 1 deletion web/helpers/atoms/Thread.atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,9 @@ export const setThreadModelParamsAtom = atom(
*/
export const activeSettingInputBoxAtom = atomWithStorage<boolean>(
ACTIVE_SETTING_INPUT_BOX,
false
false,
undefined,
{ getOnInit: true }
)

/**
Expand Down
Loading

0 comments on commit cd1a274

Please sign in to comment.