From b8d86df6882d9b638a342feb30488e5c7693c82a Mon Sep 17 00:00:00 2001 From: NamH Date: Tue, 19 Mar 2024 18:05:03 +0700 Subject: [PATCH] Fix/unable factory reset windows nitro running (#2422) * fix: unable to factory reset when nitro is running on windows --------- Signed-off-by: James --- core/src/node/api/processors/fsExt.ts | 2 +- .../conversational-extension/src/index.ts | 4 +- web/containers/Providers/DataLoader.tsx | 14 ++- web/helpers/atoms/App.atom.ts | 2 + web/hooks/useActiveModel.ts | 7 +- web/hooks/useFactoryReset.ts | 91 +++++++++++-------- .../FactoryReset/ModalConfirmReset.tsx | 17 ++-- .../Advanced/FactoryReset/ResettingModal.tsx | 29 ++++++ .../Settings/Advanced/FactoryReset/index.tsx | 2 + 9 files changed, 117 insertions(+), 51 deletions(-) create mode 100644 web/screens/Settings/Advanced/FactoryReset/ResettingModal.tsx diff --git a/core/src/node/api/processors/fsExt.ts b/core/src/node/api/processors/fsExt.ts index 9b88cfef9..7b08e24c9 100644 --- a/core/src/node/api/processors/fsExt.ts +++ b/core/src/node/api/processors/fsExt.ts @@ -101,7 +101,7 @@ export class FSExt implements Processor { }) } - rmdir(path: string): Promise { + rm(path: string): Promise { return new Promise((resolve, reject) => { fs.rm(path, { recursive: true }, (err) => { if (err) { diff --git a/extensions/conversational-extension/src/index.ts b/extensions/conversational-extension/src/index.ts index bf8c213ad..1982d90c4 100644 --- a/extensions/conversational-extension/src/index.ts +++ b/extensions/conversational-extension/src/index.ts @@ -20,9 +20,9 @@ export default class JSONConversationalExtension extends ConversationalExtension * Called when the extension is loaded. */ async onLoad() { - if (!(await fs.existsSync(JSONConversationalExtension._threadFolder))) + if (!(await fs.existsSync(JSONConversationalExtension._threadFolder))) { await fs.mkdirSync(JSONConversationalExtension._threadFolder) - console.debug('JSONConversationalExtension loaded') + } } /** diff --git a/web/containers/Providers/DataLoader.tsx b/web/containers/Providers/DataLoader.tsx index 7d38a29d6..abd0e4889 100644 --- a/web/containers/Providers/DataLoader.tsx +++ b/web/containers/Providers/DataLoader.tsx @@ -2,7 +2,7 @@ import { Fragment, ReactNode, useEffect } from 'react' -import { AppConfiguration } from '@janhq/core' +import { AppConfiguration, getUserHomePath, joinPath } from '@janhq/core' import { useSetAtom } from 'jotai' import useAssistants from '@/hooks/useAssistants' @@ -10,6 +10,7 @@ import useGetSystemResources from '@/hooks/useGetSystemResources' import useModels from '@/hooks/useModels' import useThreads from '@/hooks/useThreads' +import { defaultJanDataFolderAtom } from '@/helpers/atoms/App.atom' import { janDataFolderPathAtom, quickAskEnabledAtom, @@ -22,6 +23,7 @@ type Props = { const DataLoader: React.FC = ({ children }) => { const setJanDataFolderPath = useSetAtom(janDataFolderPathAtom) const setQuickAskEnabled = useSetAtom(quickAskEnabledAtom) + const setJanDefaultDataFolder = useSetAtom(defaultJanDataFolderAtom) useModels() useThreads() @@ -37,6 +39,16 @@ const DataLoader: React.FC = ({ children }) => { }) }, [setJanDataFolderPath, setQuickAskEnabled]) + useEffect(() => { + async function getDefaultJanDataFolder() { + const homePath = await getUserHomePath() + const defaultJanDataFolder = await joinPath([homePath, 'jan']) + + setJanDefaultDataFolder(defaultJanDataFolder) + } + getDefaultJanDataFolder() + }, [setJanDefaultDataFolder]) + console.debug('Load Data...') return {children} diff --git a/web/helpers/atoms/App.atom.ts b/web/helpers/atoms/App.atom.ts index 342c04819..b17d43db1 100644 --- a/web/helpers/atoms/App.atom.ts +++ b/web/helpers/atoms/App.atom.ts @@ -3,3 +3,5 @@ import { atom } from 'jotai' import { MainViewState } from '@/constants/screens' export const mainViewStateAtom = atom(MainViewState.Thread) + +export const defaultJanDataFolderAtom = atom('') diff --git a/web/hooks/useActiveModel.ts b/web/hooks/useActiveModel.ts index e6c519f9f..98433c2ea 100644 --- a/web/hooks/useActiveModel.ts +++ b/web/hooks/useActiveModel.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react' +import { useCallback, useEffect, useRef } from 'react' import { events, Model, ModelEvent } from '@janhq/core' import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai' @@ -86,13 +86,12 @@ export function useActiveModel() { events.emit(ModelEvent.OnModelInit, model) } - const stopModel = async () => { + const stopModel = useCallback(async () => { if (activeModel) { - setActiveModel(undefined) setStateModel({ state: 'stop', loading: true, model: activeModel.id }) events.emit(ModelEvent.OnModelStop, activeModel) } - } + }, [activeModel, setStateModel]) return { activeModel, startModel, stopModel, stateModel } } diff --git a/web/hooks/useFactoryReset.ts b/web/hooks/useFactoryReset.ts index da0813060..878461ef1 100644 --- a/web/hooks/useFactoryReset.ts +++ b/web/hooks/useFactoryReset.ts @@ -1,49 +1,68 @@ -import { useEffect, useState } from 'react' +import { useCallback } from 'react' -import { fs, AppConfiguration, joinPath, getUserHomePath } from '@janhq/core' +import { fs, AppConfiguration } from '@janhq/core' +import { atom, useAtomValue, useSetAtom } from 'jotai' + +import { useActiveModel } from './useActiveModel' + +import { defaultJanDataFolderAtom } from '@/helpers/atoms/App.atom' + +export enum FactoryResetState { + Idle = 'idle', + Starting = 'starting', + StoppingModel = 'stopping_model', + DeletingData = 'deleting_data', + ClearLocalStorage = 'clear_local_storage', +} + +export const factoryResetStateAtom = atom(FactoryResetState.Idle) export default function useFactoryReset() { - const [defaultJanDataFolder, setdefaultJanDataFolder] = useState('') + const defaultJanDataFolder = useAtomValue(defaultJanDataFolderAtom) + const { activeModel, stopModel } = useActiveModel() + const setFactoryResetState = useSetAtom(factoryResetStateAtom) - useEffect(() => { - async function getDefaultJanDataFolder() { - const homePath = await getUserHomePath() - const defaultJanDataFolder = await joinPath([homePath, 'jan']) - setdefaultJanDataFolder(defaultJanDataFolder) - } - getDefaultJanDataFolder() - }, []) + const resetAll = useCallback( + async (keepCurrentFolder?: boolean) => { + setFactoryResetState(FactoryResetState.Starting) + // read the place of jan data folder + const appConfiguration: AppConfiguration | undefined = + await window.core?.api?.getAppConfigurations() - const resetAll = async (keepCurrentFolder?: boolean) => { - // read the place of jan data folder - const appConfiguration: AppConfiguration | undefined = - await window.core?.api?.getAppConfigurations() - - if (!appConfiguration) { - console.debug('Failed to get app configuration') - } - - console.debug('appConfiguration: ', appConfiguration) - const janDataFolderPath = appConfiguration!.data_folder - - if (!keepCurrentFolder) { - // set the default jan data folder to user's home directory - const configuration: AppConfiguration = { - data_folder: defaultJanDataFolder, - quick_ask: appConfiguration?.quick_ask ?? false, + if (!appConfiguration) { + console.debug('Failed to get app configuration') } - await window.core?.api?.updateAppConfiguration(configuration) - } - await fs.rmdirSync(janDataFolderPath, { recursive: true }) - // reset the localStorage - localStorage.clear() + const janDataFolderPath = appConfiguration!.data_folder - await window.core?.api?.relaunch() - } + if (!keepCurrentFolder) { + // set the default jan data folder to user's home directory + const configuration: AppConfiguration = { + data_folder: defaultJanDataFolder, + quick_ask: appConfiguration?.quick_ask ?? false, + } + await window.core?.api?.updateAppConfiguration(configuration) + } + + if (activeModel) { + setFactoryResetState(FactoryResetState.StoppingModel) + await stopModel() + await new Promise((resolve) => setTimeout(resolve, 4000)) + } + + setFactoryResetState(FactoryResetState.DeletingData) + await fs.rm(janDataFolderPath) + + setFactoryResetState(FactoryResetState.ClearLocalStorage) + // reset the localStorage + localStorage.clear() + + await window.core?.api?.relaunch() + }, + [defaultJanDataFolder, activeModel, stopModel, setFactoryResetState] + ) return { - defaultJanDataFolder, resetAll, } } diff --git a/web/screens/Settings/Advanced/FactoryReset/ModalConfirmReset.tsx b/web/screens/Settings/Advanced/FactoryReset/ModalConfirmReset.tsx index 4560ac1ad..a50b17895 100644 --- a/web/screens/Settings/Advanced/FactoryReset/ModalConfirmReset.tsx +++ b/web/screens/Settings/Advanced/FactoryReset/ModalConfirmReset.tsx @@ -11,21 +11,25 @@ import { Checkbox, Input, } from '@janhq/uikit' -import { atom, useAtom } from 'jotai' +import { atom, useAtom, useAtomValue } from 'jotai' import useFactoryReset from '@/hooks/useFactoryReset' +import { defaultJanDataFolderAtom } from '@/helpers/atoms/App.atom' + export const modalValidationAtom = atom(false) const ModalConfirmReset = () => { const [modalValidation, setModalValidation] = useAtom(modalValidationAtom) - const { resetAll, defaultJanDataFolder } = useFactoryReset() + const defaultJanDataFolder = useAtomValue(defaultJanDataFolderAtom) + const { resetAll } = useFactoryReset() const [inputValue, setInputValue] = useState('') const [currentDirectoryChecked, setCurrentDirectoryChecked] = useState(true) - const onFactoryResetClick = useCallback( - () => resetAll(currentDirectoryChecked), - [currentDirectoryChecked, resetAll] - ) + + const onFactoryResetClick = useCallback(() => { + setModalValidation(false) + resetAll(currentDirectoryChecked) + }, [currentDirectoryChecked, resetAll, setModalValidation]) return ( {

Otherwise it will reset back to its original location at:{' '} - {/* TODO should be from system */} {defaultJanDataFolder}

diff --git a/web/screens/Settings/Advanced/FactoryReset/ResettingModal.tsx b/web/screens/Settings/Advanced/FactoryReset/ResettingModal.tsx new file mode 100644 index 000000000..6469f6416 --- /dev/null +++ b/web/screens/Settings/Advanced/FactoryReset/ResettingModal.tsx @@ -0,0 +1,29 @@ +import { Modal, ModalContent, ModalHeader, ModalTitle } from '@janhq/uikit' +import { atom, useAtomValue } from 'jotai' + +import { + FactoryResetState, + factoryResetStateAtom, +} from '@/hooks/useFactoryReset' + +const resetModalVisibilityAtom = atom((get) => { + const visible = get(factoryResetStateAtom) !== FactoryResetState.Idle + return visible +}) + +const ResettingModal: React.FC = () => { + const visibility = useAtomValue(resetModalVisibilityAtom) + + return ( + + + + Factory reset in progress.. + +

Resetting..

+
+
+ ) +} + +export default ResettingModal diff --git a/web/screens/Settings/Advanced/FactoryReset/index.tsx b/web/screens/Settings/Advanced/FactoryReset/index.tsx index e7b1e2995..fb6bf8b6f 100644 --- a/web/screens/Settings/Advanced/FactoryReset/index.tsx +++ b/web/screens/Settings/Advanced/FactoryReset/index.tsx @@ -3,6 +3,7 @@ import { Button } from '@janhq/uikit' import { useSetAtom } from 'jotai' import ModalValidation, { modalValidationAtom } from './ModalConfirmReset' +import ResettingModal from './ResettingModal' const FactoryReset = () => { const setModalValidation = useSetAtom(modalValidationAtom) @@ -30,6 +31,7 @@ const FactoryReset = () => { Reset + ) }