From 5e13fd2f53bb6127770b053f1a922db2eb428999 Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Tue, 30 Jan 2024 16:36:58 +0700 Subject: [PATCH 1/4] fix: loader error change folder --- web/containers/Layout/index.tsx | 3 +- web/hooks/useVaultDirectory.ts | 87 ------------------- .../DataFolder/ModalSameDirectory.tsx | 15 +++- .../Settings/Advanced/DataFolder/index.tsx | 5 +- web/screens/Settings/index.tsx | 4 +- 5 files changed, 20 insertions(+), 94 deletions(-) delete mode 100644 web/hooks/useVaultDirectory.ts diff --git a/web/containers/Layout/index.tsx b/web/containers/Layout/index.tsx index e7bde49c0..77a1fe971 100644 --- a/web/containers/Layout/index.tsx +++ b/web/containers/Layout/index.tsx @@ -12,7 +12,8 @@ import TopBar from '@/containers/Layout/TopBar' import { MainViewState } from '@/constants/screens' import { useMainViewState } from '@/hooks/useMainViewState' -import { SUCCESS_SET_NEW_DESTINATION } from '@/hooks/useVaultDirectory' + +import { SUCCESS_SET_NEW_DESTINATION } from '@/screens/Settings/Advanced/DataFolder' const BaseLayout = (props: PropsWithChildren) => { const { children } = props diff --git a/web/hooks/useVaultDirectory.ts b/web/hooks/useVaultDirectory.ts deleted file mode 100644 index 9d7adf2ab..000000000 --- a/web/hooks/useVaultDirectory.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { useEffect, useState } from 'react' - -import { fs, AppConfiguration } from '@janhq/core' - -export const SUCCESS_SET_NEW_DESTINATION = 'successSetNewDestination' - -export function useVaultDirectory() { - const [isSameDirectory, setIsSameDirectory] = useState(false) - const [isDirectoryConfirm, setIsDirectoryConfirm] = useState(false) - const [isErrorSetNewDest, setIsErrorSetNewDest] = useState(false) - const [currentPath, setCurrentPath] = useState('') - const [newDestinationPath, setNewDestinationPath] = useState('') - - useEffect(() => { - window.core?.api - ?.getAppConfigurations() - ?.then((appConfig: AppConfiguration) => { - setCurrentPath(appConfig.data_folder) - }) - }, []) - - const setNewDestination = async () => { - const destFolder = await window.core?.api?.selectDirectory() - setNewDestinationPath(destFolder) - - if (destFolder) { - console.debug(`Destination folder selected: ${destFolder}`) - try { - const appConfiguration: AppConfiguration = - await window.core?.api?.getAppConfigurations() - const currentJanDataFolder = appConfiguration.data_folder - - if (currentJanDataFolder === destFolder) { - console.debug( - `Destination folder is the same as current folder. Ignore..` - ) - setIsSameDirectory(true) - setIsDirectoryConfirm(false) - return - } else { - setIsSameDirectory(false) - setIsDirectoryConfirm(true) - } - setIsErrorSetNewDest(false) - } catch (e) { - console.error(`Error: ${e}`) - setIsErrorSetNewDest(true) - } - } - } - - const applyNewDestination = async () => { - try { - const appConfiguration: AppConfiguration = - await window.core?.api?.getAppConfigurations() - const currentJanDataFolder = appConfiguration.data_folder - - appConfiguration.data_folder = newDestinationPath - - await fs.syncFile(currentJanDataFolder, newDestinationPath) - await window.core?.api?.updateAppConfiguration(appConfiguration) - console.debug( - `File sync finished from ${currentPath} to ${newDestinationPath}` - ) - - setIsErrorSetNewDest(false) - localStorage.setItem(SUCCESS_SET_NEW_DESTINATION, 'true') - await window.core?.api?.relaunch() - } catch (e) { - console.error(`Error: ${e}`) - setIsErrorSetNewDest(true) - } - } - - return { - setNewDestination, - newDestinationPath, - applyNewDestination, - isSameDirectory, - setIsDirectoryConfirm, - isDirectoryConfirm, - setIsSameDirectory, - currentPath, - isErrorSetNewDest, - setIsErrorSetNewDest, - } -} diff --git a/web/screens/Settings/Advanced/DataFolder/ModalSameDirectory.tsx b/web/screens/Settings/Advanced/DataFolder/ModalSameDirectory.tsx index 8b2d90c61..1909e6428 100644 --- a/web/screens/Settings/Advanced/DataFolder/ModalSameDirectory.tsx +++ b/web/screens/Settings/Advanced/DataFolder/ModalSameDirectory.tsx @@ -15,7 +15,11 @@ import { atom, useAtom } from 'jotai' export const showSamePathModalAtom = atom(false) -const ModalSameDirectory = () => { +type Props = { + onChangeFolderClick: () => void +} + +const ModalSameDirectory = ({ onChangeFolderClick }: Props) => { const [show, setShow] = useAtom(showSamePathModalAtom) return ( @@ -34,7 +38,14 @@ const ModalSameDirectory = () => { - diff --git a/web/screens/Settings/Advanced/DataFolder/index.tsx b/web/screens/Settings/Advanced/DataFolder/index.tsx index 4b242f235..e653e4b9b 100644 --- a/web/screens/Settings/Advanced/DataFolder/index.tsx +++ b/web/screens/Settings/Advanced/DataFolder/index.tsx @@ -7,7 +7,7 @@ import { PencilIcon, FolderOpenIcon } from 'lucide-react' import Loader from '@/containers/Loader' -import { SUCCESS_SET_NEW_DESTINATION } from '@/hooks/useVaultDirectory' +export const SUCCESS_SET_NEW_DESTINATION = 'successSetNewDestination' import ModalChangeDirectory, { showDirectoryConfirmModalAtom, @@ -67,6 +67,7 @@ const DataFolder = () => { await window.core?.api?.relaunch() } catch (e) { console.error(`Error: ${e}`) + setShowLoader(false) setShowChangeFolderError(true) } }, [destinationPath, setShowChangeFolderError]) @@ -107,7 +108,7 @@ const DataFolder = () => { - + { const [activeStaticMenu, setActiveStaticMenu] = useState('My Models') const [menus, setMenus] = useState([]) From 00c4397be66901545353b11e56a67fbb549b734e Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Tue, 30 Jan 2024 16:43:58 +0700 Subject: [PATCH 2/4] fix: loader error change folder --- .../Advanced/DataFolder/ModalErrorSetDestGlobal.tsx | 7 +++++-- web/screens/Settings/Advanced/DataFolder/index.tsx | 9 +++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx b/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx index 3729dc0d8..125cd18bd 100644 --- a/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx +++ b/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx @@ -10,13 +10,15 @@ import { ModalClose, Button, } from '@janhq/uikit' -import { atom, useAtom } from 'jotai' +import { atom, useAtom, useAtomValue } from 'jotai' + +import { errorAtom } from '.' export const showChangeFolderErrorAtom = atom(false) const ModalErrorSetDestGlobal = () => { const [show, setShow] = useAtom(showChangeFolderErrorAtom) - + const error = useAtomValue(errorAtom) return ( @@ -28,6 +30,7 @@ const ModalErrorSetDestGlobal = () => { Oops! Something went wrong. Jan data folder remains the same. Please try again.

+

{error}

setShow(false)}> diff --git a/web/screens/Settings/Advanced/DataFolder/index.tsx b/web/screens/Settings/Advanced/DataFolder/index.tsx index e653e4b9b..b7bb88cdd 100644 --- a/web/screens/Settings/Advanced/DataFolder/index.tsx +++ b/web/screens/Settings/Advanced/DataFolder/index.tsx @@ -2,7 +2,7 @@ import { Fragment, useCallback, useEffect, useState } from 'react' import { fs, AppConfiguration } from '@janhq/core' import { Button, Input } from '@janhq/uikit' -import { useSetAtom } from 'jotai' +import { atom, useSetAtom } from 'jotai' import { PencilIcon, FolderOpenIcon } from 'lucide-react' import Loader from '@/containers/Loader' @@ -18,6 +18,8 @@ import ModalErrorSetDestGlobal, { import ModalSameDirectory, { showSamePathModalAtom } from './ModalSameDirectory' +export const errorAtom = atom('') + const DataFolder = () => { const [janDataFolderPath, setJanDataFolderPath] = useState('') const [showLoader, setShowLoader] = useState(false) @@ -25,6 +27,7 @@ const DataFolder = () => { const setShowSameDirectory = useSetAtom(showSamePathModalAtom) const setShowChangeFolderError = useSetAtom(showChangeFolderErrorAtom) const [destinationPath, setDestinationPath] = useState(undefined) + const setError = useSetAtom(errorAtom) useEffect(() => { window.core?.api @@ -65,8 +68,10 @@ const DataFolder = () => { setShowLoader(false) }, 1200) await window.core?.api?.relaunch() - } catch (e) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { console.error(`Error: ${e}`) + setError(e.message) setShowLoader(false) setShowChangeFolderError(true) } From 96aded6b035a284e1b12c0c23325dcb53c0cd2e3 Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Tue, 30 Jan 2024 17:04:56 +0700 Subject: [PATCH 3/4] fix: showing catch error on modal when change folder --- .../Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx | 2 +- web/screens/Settings/Advanced/DataFolder/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx b/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx index 125cd18bd..d80e34f3c 100644 --- a/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx +++ b/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx @@ -30,7 +30,7 @@ const ModalErrorSetDestGlobal = () => { Oops! Something went wrong. Jan data folder remains the same. Please try again.

-

{error}

+

{error}

setShow(false)}> diff --git a/web/screens/Settings/Advanced/DataFolder/index.tsx b/web/screens/Settings/Advanced/DataFolder/index.tsx index b7bb88cdd..1704cd964 100644 --- a/web/screens/Settings/Advanced/DataFolder/index.tsx +++ b/web/screens/Settings/Advanced/DataFolder/index.tsx @@ -75,7 +75,7 @@ const DataFolder = () => { setShowLoader(false) setShowChangeFolderError(true) } - }, [destinationPath, setShowChangeFolderError]) + }, [destinationPath, setError, setShowChangeFolderError]) return ( From 282dd58d0519c92ab5a335b3c5b1f60f8fe6f262 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 30 Jan 2024 23:03:20 +0700 Subject: [PATCH 4/4] fix: not allow user to choose sub directory as jan data folder Signed-off-by: James --- core/src/api/index.ts | 1 + core/src/core.ts | 20 ++++++++++++++-- electron/handlers/app.ts | 23 ++++++++++++++++++- .../DataFolder/ModalErrorSetDestGlobal.tsx | 6 +---- .../Settings/Advanced/DataFolder/index.tsx | 22 ++++++++++-------- 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/core/src/api/index.ts b/core/src/api/index.ts index a232c4090..0adc8b7e2 100644 --- a/core/src/api/index.ts +++ b/core/src/api/index.ts @@ -12,6 +12,7 @@ export enum AppRoute { updateAppConfiguration = 'updateAppConfiguration', relaunch = 'relaunch', joinPath = 'joinPath', + isSubdirectory = 'isSubdirectory', baseName = 'baseName', startServer = 'startServer', stopServer = 'stopServer', diff --git a/core/src/core.ts b/core/src/core.ts index aa545e10e..24053e55c 100644 --- a/core/src/core.ts +++ b/core/src/core.ts @@ -22,7 +22,11 @@ const executeOnMain: (extension: string, method: string, ...args: any[]) => Prom * @param {object} network - Optional object to specify proxy/whether to ignore SSL certificates. * @returns {Promise} A promise that resolves when the file is downloaded. */ -const downloadFile: (url: string, fileName: string, network?: { proxy?: string, ignoreSSL?: boolean }) => Promise = (url, fileName, network) => { +const downloadFile: ( + url: string, + fileName: string, + network?: { proxy?: string; ignoreSSL?: boolean } +) => Promise = (url, fileName, network) => { return global.core?.api?.downloadFile(url, fileName, network) } @@ -87,6 +91,17 @@ const getResourcePath: () => Promise = () => global.core.api?.getResourc const log: (message: string, fileName?: string) => void = (message, fileName) => global.core.api?.log(message, fileName) +/** + * Check whether the path is a subdirectory of another path. + * + * @param from - The path to check. + * @param to - The path to check against. + * + * @returns {Promise} - A promise that resolves with a boolean indicating whether the path is a subdirectory. + */ +const isSubdirectory: (from: string, to: string) => Promise = (from: string, to: string) => + global.core.api?.isSubdirectory(from, to) + /** * Register extension point function type definition */ @@ -94,7 +109,7 @@ export type RegisterExtensionPoint = ( extensionName: string, extensionId: string, method: Function, - priority?: number, + priority?: number ) => void /** @@ -111,5 +126,6 @@ export { openExternalUrl, baseName, log, + isSubdirectory, FileStat, } diff --git a/electron/handlers/app.ts b/electron/handlers/app.ts index bdb70047a..c1f431ef3 100644 --- a/electron/handlers/app.ts +++ b/electron/handlers/app.ts @@ -1,5 +1,5 @@ import { app, ipcMain, dialog, shell } from 'electron' -import { join, basename } from 'path' +import { join, basename, relative as getRelative, isAbsolute } from 'path' import { WindowManager } from './../managers/window' import { getResourcePath } from './../utils/path' import { AppRoute, AppConfiguration } from '@janhq/core' @@ -50,6 +50,27 @@ export function handleAppIPCs() { join(...paths) ) + /** + * Checks if the given path is a subdirectory of the given directory. + * + * @param _event - The IPC event object. + * @param from - The path to check. + * @param to - The directory to check against. + * + * @returns {Promise} - A promise that resolves with the result. + */ + ipcMain.handle( + AppRoute.isSubdirectory, + async (_event, from: string, to: string) => { + const relative = getRelative(from, to) + const isSubdir = + relative && !relative.startsWith('..') && !isAbsolute(relative) + + if (isSubdir === '') return false + else return isSubdir + } + ) + /** * Retrieve basename from given path, respect to the current OS. */ diff --git a/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx b/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx index d80e34f3c..84646e735 100644 --- a/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx +++ b/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx @@ -10,15 +10,12 @@ import { ModalClose, Button, } from '@janhq/uikit' -import { atom, useAtom, useAtomValue } from 'jotai' - -import { errorAtom } from '.' +import { atom, useAtom } from 'jotai' export const showChangeFolderErrorAtom = atom(false) const ModalErrorSetDestGlobal = () => { const [show, setShow] = useAtom(showChangeFolderErrorAtom) - const error = useAtomValue(errorAtom) return ( @@ -30,7 +27,6 @@ const ModalErrorSetDestGlobal = () => { Oops! Something went wrong. Jan data folder remains the same. Please try again.

-

{error}

setShow(false)}> diff --git a/web/screens/Settings/Advanced/DataFolder/index.tsx b/web/screens/Settings/Advanced/DataFolder/index.tsx index 1704cd964..5abd5390b 100644 --- a/web/screens/Settings/Advanced/DataFolder/index.tsx +++ b/web/screens/Settings/Advanced/DataFolder/index.tsx @@ -1,8 +1,8 @@ import { Fragment, useCallback, useEffect, useState } from 'react' -import { fs, AppConfiguration } from '@janhq/core' +import { fs, AppConfiguration, isSubdirectory } from '@janhq/core' import { Button, Input } from '@janhq/uikit' -import { atom, useSetAtom } from 'jotai' +import { useSetAtom } from 'jotai' import { PencilIcon, FolderOpenIcon } from 'lucide-react' import Loader from '@/containers/Loader' @@ -18,8 +18,6 @@ import ModalErrorSetDestGlobal, { import ModalSameDirectory, { showSamePathModalAtom } from './ModalSameDirectory' -export const errorAtom = atom('') - const DataFolder = () => { const [janDataFolderPath, setJanDataFolderPath] = useState('') const [showLoader, setShowLoader] = useState(false) @@ -27,7 +25,6 @@ const DataFolder = () => { const setShowSameDirectory = useSetAtom(showSamePathModalAtom) const setShowChangeFolderError = useSetAtom(showChangeFolderErrorAtom) const [destinationPath, setDestinationPath] = useState(undefined) - const setError = useSetAtom(errorAtom) useEffect(() => { window.core?.api @@ -46,6 +43,15 @@ const DataFolder = () => { return } + const appConfiguration: AppConfiguration = + await window.core?.api?.getAppConfigurations() + const currentJanDataFolder = appConfiguration.data_folder + + if (await isSubdirectory(currentJanDataFolder, destFolder)) { + setShowSameDirectory(true) + return + } + setDestinationPath(destFolder) setShowDirectoryConfirm(true) }, [janDataFolderPath, setShowSameDirectory, setShowDirectoryConfirm]) @@ -68,14 +74,12 @@ const DataFolder = () => { setShowLoader(false) }, 1200) await window.core?.api?.relaunch() - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e: any) { + } catch (e) { console.error(`Error: ${e}`) - setError(e.message) setShowLoader(false) setShowChangeFolderError(true) } - }, [destinationPath, setError, setShowChangeFolderError]) + }, [destinationPath, setShowChangeFolderError]) return (