From e0a96283ac684fe5e15d76082fa6317732cedb9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D1=81=D0=B8=D0=BB=D0=B8=D0=B9=20=D0=95=D0=BB?= =?UTF-8?q?=D0=B8=D1=81=D0=B5=D0=B5=D0=B2?= Date: Mon, 14 Sep 2026 09:55:30 +0600 Subject: [PATCH 1/2] feat: add can use folders hook --- apps/mobile/app/(main)/chat/list.tsx | 4 ++- .../search-folder-view/component.tsx | 25 +++++++++++------- .../features/menu-list/src/lib/component.tsx | 17 +++++++++--- .../use-folder-search-list/src/lib/hook.ts | 26 ++++++++++++------- .../src/lib/component.tsx | 4 ++- .../src/lib/component.tsx | 7 +++-- .../features/use-can-use-folders/.babelrc | 12 +++++++++ .../use-can-use-folders/eslint.config.cjs | 12 +++++++++ .../features/use-can-use-folders/project.json | 9 +++++++ .../features/use-can-use-folders/src/hook.ts | 22 ++++++++++++++++ .../features/use-can-use-folders/src/index.ts | 1 + .../use-can-use-folders/tsconfig.json | 17 ++++++++++++ .../use-can-use-folders/tsconfig.lib.json | 19 ++++++++++++++ .../lib/app-configuration/models/features.ts | 3 +++ .../permissions/features-permissions.ts | 3 +++ .../data-access/api/src/lib/folders/api.ts | 2 +- tsconfig.base.json | 3 +++ 17 files changed, 157 insertions(+), 29 deletions(-) create mode 100644 libs/mobile/shared/features/use-can-use-folders/.babelrc create mode 100644 libs/mobile/shared/features/use-can-use-folders/eslint.config.cjs create mode 100644 libs/mobile/shared/features/use-can-use-folders/project.json create mode 100644 libs/mobile/shared/features/use-can-use-folders/src/hook.ts create mode 100644 libs/mobile/shared/features/use-can-use-folders/src/index.ts create mode 100644 libs/mobile/shared/features/use-can-use-folders/tsconfig.json create mode 100644 libs/mobile/shared/features/use-can-use-folders/tsconfig.lib.json diff --git a/apps/mobile/app/(main)/chat/list.tsx b/apps/mobile/app/(main)/chat/list.tsx index b8ab24d1..34e9b7c6 100644 --- a/apps/mobile/app/(main)/chat/list.tsx +++ b/apps/mobile/app/(main)/chat/list.tsx @@ -11,6 +11,7 @@ import { UpsertFolderSheet, UpsertFolderSheetMethods, } from '@open-webui-react-native/mobile/folder/features/upsert-folder-sheet'; +import { useCanUseFolders } from '@open-webui-react-native/mobile/shared/features/use-can-use-folders'; import { ScreenWrapper } from '@open-webui-react-native/mobile/shared/ui/screen-wrapper'; import { AppHeader, AppPressable, Avatar, IconButton, View } from '@open-webui-react-native/mobile/shared/ui/ui-kit'; import { navigationConfig } from '@open-webui-react-native/mobile/shared/utils/navigation'; @@ -29,6 +30,7 @@ export default function ChatListScreen(): ReactElement { const upsertFolderSheetRef = useRef(null); const shareFolderSheetRef = useRef(null); const { data: profile } = authApi.useGetProfile(); + const { canUseFolders } = useCanUseFolders(); const handleChatPress = (id: string): void => navigateOnce(navigationConfig.main.chat.view({ id })); @@ -68,7 +70,7 @@ export default function ChatListScreen(): ReactElement { } accessoryRight={ - {isFeatureEnabled(FeatureID.CHAT_FOLDERS) && ( + {isFeatureEnabled(FeatureID.CHAT_FOLDERS) && canUseFolders && ( ( )} - } - selectedItemId={selectedItemId} - renderTrigger={renderTrigger} - searchPlaceholder={translate('TEXT_SEARCH_FOLDER')} - {...props} - /> + {canUseFolders && ( + } + selectedItemId={selectedItemId} + renderTrigger={renderTrigger} + searchPlaceholder={translate('TEXT_SEARCH_FOLDER')} + {...props} + /> + )} ); } diff --git a/libs/mobile/chat/features/menu-list/src/lib/component.tsx b/libs/mobile/chat/features/menu-list/src/lib/component.tsx index 5dd64c52..ba79688c 100644 --- a/libs/mobile/chat/features/menu-list/src/lib/component.tsx +++ b/libs/mobile/chat/features/menu-list/src/lib/component.tsx @@ -5,6 +5,7 @@ import { ChatActionsMenuSheet, ChatActionsMenuSheetMethods, } from '@open-webui-react-native/mobile/shared/features/chat-actions-menu-sheet'; +import { useCanUseFolders } from '@open-webui-react-native/mobile/shared/features/use-can-use-folders'; import { ChatListRow } from '@open-webui-react-native/mobile/shared/ui/chat-list-row'; import { DateSectionList } from '@open-webui-react-native/mobile/shared/ui/date-section-list'; import { @@ -44,6 +45,8 @@ export function ChatMenuList({ const [isFirstLoading, setIsFirstLoading] = useState(true); + const { canUseFolders } = useCanUseFolders(); + const { data: chats, isFetchingNextPage, @@ -63,20 +66,26 @@ export function ChatMenuList({ isLoading: isFoldersLoading, isRefetching: isFoldersRefetching, refetch: refetchFolders, - } = foldersApi.useGetFolders(); + } = foldersApi.useGetFolders({ enabled: canUseFolders }); const { data: sharedFolders, isLoading: isSharedFoldersLoading, isRefetching: isSharedFoldersRefetching, refetch: refetchSharedFolders, - } = foldersApi.useGetSharedFolders(); + } = foldersApi.useGetSharedFolders({ enabled: canUseFolders }); const isLoading = isChatsLoading || isPinnedChatsLoading || isFoldersLoading || isSharedFoldersLoading || isFirstLoading; const isRefetching = isChatsRefetching || isPinnedChatsRefetching || isFoldersRefetching || isSharedFoldersRefetching; const refetch = (): void => { - Promise.all([refetchChats(), refetchPinnedChats(), refetchFolders(), refetchSharedFolders()]); + // NOTE: react-query's refetch() runs even for a disabled query, so the folders feature gate has + // to be re-checked here too, or a pull-to-refresh would still hit the forbidden endpoint. + Promise.all([ + refetchChats(), + refetchPinnedChats(), + ...(canUseFolders ? [refetchFolders(), refetchSharedFolders()] : []), + ]); }; useEffect(() => { @@ -113,7 +122,7 @@ export function ChatMenuList({ refreshControl={} ListHeaderComponent={ - {isFeatureEnabled(FeatureID.CHAT_FOLDERS) && ( + {isFeatureEnabled(FeatureID.CHAT_FOLDERS) && canUseFolders && ( {/* NOTE: A folder owned by somebody else cannot be renamed or deleted by the recipient, so its row offers no actions on long press. */} diff --git a/libs/mobile/chat/utils/use-folder-search-list/src/lib/hook.ts b/libs/mobile/chat/utils/use-folder-search-list/src/lib/hook.ts index d6da6e07..7ceada1d 100644 --- a/libs/mobile/chat/utils/use-folder-search-list/src/lib/hook.ts +++ b/libs/mobile/chat/utils/use-folder-search-list/src/lib/hook.ts @@ -11,6 +11,7 @@ interface UseFolderSearchListParams { noFolderText: string; createFolderText: string; onCreateFolderPress: () => void; + canCreateFolder?: boolean; } interface UseFolderSearchListResult { @@ -22,6 +23,7 @@ export function useFolderSearchList({ noFolderText, createFolderText, onCreateFolderPress, + canCreateFolder = true, }: UseFolderSearchListParams): UseFolderSearchListResult { const { isDarkColorScheme } = useColorScheme(); @@ -32,17 +34,21 @@ export function useFolderSearchList({ name: noFolderText, iconName: isDarkColorScheme ? ('logoSmallDark' as IconName) : ('logoSmallLight' as IconName), }, - { - id: MockFolderItemIds.CREATE_FOLDER_ID, - name: createFolderText, - onPress: onCreateFolderPress, - iconName: 'folderPlus' as IconName, - containerClassName: 'mb-24', - textClassName: 'text-brand-primary', - iconClassName: 'color-brand-primary', - }, + ...(canCreateFolder + ? [ + { + id: MockFolderItemIds.CREATE_FOLDER_ID, + name: createFolderText, + onPress: onCreateFolderPress, + iconName: 'folderPlus' as IconName, + containerClassName: 'mb-24', + textClassName: 'text-brand-primary', + iconClassName: 'color-brand-primary', + }, + ] + : []), ], - [isDarkColorScheme, noFolderText, createFolderText, onCreateFolderPress], + [isDarkColorScheme, canCreateFolder, noFolderText, createFolderText, onCreateFolderPress], ); return { emptyFolders, createFolderId: MockFolderItemIds.CREATE_FOLDER_ID }; diff --git a/libs/mobile/folder/features/folder-actions-sheet/src/lib/component.tsx b/libs/mobile/folder/features/folder-actions-sheet/src/lib/component.tsx index 2d162a95..1d9656b2 100644 --- a/libs/mobile/folder/features/folder-actions-sheet/src/lib/component.tsx +++ b/libs/mobile/folder/features/folder-actions-sheet/src/lib/component.tsx @@ -2,6 +2,7 @@ import { BottomSheetModal } from '@gorhom/bottom-sheet'; import { useTranslation } from '@ronas-it/react-native-common-modules/i18n'; import { ForwardedRef, ReactElement, useImperativeHandle, useRef, useState } from 'react'; import { fileSystemService } from '@open-webui-react-native/mobile/shared/data-access/file-system-service'; +import { useCanUseFolders } from '@open-webui-react-native/mobile/shared/features/use-can-use-folders'; import { ActionsBottomSheet, ActionsBottomSheetProps, @@ -39,7 +40,8 @@ export function FolderActionsSheet({ onEditPress, onSharePress, ref }: FolderAct const [isExportLoading, setIsExportLoading] = useState(false); const { data: profile } = authApi.useGetProfile(); - const { data: sharedFolders } = foldersApi.useGetSharedFolders(); + const { canUseFolders } = useCanUseFolders(); + const { data: sharedFolders } = foldersApi.useGetSharedFolders({ enabled: canUseFolders }); const { mutateAsync: deleteFolder, isPending: isDeleting } = foldersApi.useDeleteFolder(); // NOTE: The shared list holds exactly the folders owned by somebody else, so a folder missing from diff --git a/libs/mobile/shared/features/chat-actions-menu-sheet/src/lib/component.tsx b/libs/mobile/shared/features/chat-actions-menu-sheet/src/lib/component.tsx index 17250173..31c74821 100644 --- a/libs/mobile/shared/features/chat-actions-menu-sheet/src/lib/component.tsx +++ b/libs/mobile/shared/features/chat-actions-menu-sheet/src/lib/component.tsx @@ -12,6 +12,7 @@ import { UpsertFolderSheetMethods, } from '@open-webui-react-native/mobile/folder/features/upsert-folder-sheet'; import { DownloadChatOptionsSheet } from '@open-webui-react-native/mobile/shared/features/download-chat-options-sheet'; +import { useCanUseFolders } from '@open-webui-react-native/mobile/shared/features/use-can-use-folders'; import { ActionButtonsModal, ActionButtonsModalMethods, @@ -64,7 +65,8 @@ export function ChatActionsMenuSheet({ goToChat, isPinned, ref, isInChat }: Chat const { mutateAsync: cloneChat, isPending: isCloning } = chatApi.useCloneChat(); const { mutateAsync: archiveChat, isPending: isArchiving } = chatApi.useArchiveChat(); const { mutateAsync: unarchiveChat, isPending: isUnarchiving } = chatApi.useUnarchiveChat(); - const { data: folders } = foldersApi.useGetFolders(); + const { canUseFolders } = useCanUseFolders(); + const { data: folders } = foldersApi.useGetFolders({ enabled: canUseFolders }); const [activeChat, setActiveChat] = useState(null); const [folderId, setFolderId] = useState(undefined); @@ -105,6 +107,7 @@ export function ChatActionsMenuSheet({ goToChat, isPinned, ref, isInChat }: Chat noFolderText: translate('MOVE_CHAT_TO_FOLDER_MODAL.TEXT_NO_FOLDER'), createFolderText: translate('MOVE_CHAT_TO_FOLDER_MODAL.TEXT_CREATE_NEW_FOLDER'), onCreateFolderPress: openCreateFolderModal, + canCreateFolder: canUseFolders, }); const handleAction = useMemo( @@ -236,7 +239,7 @@ export function ChatActionsMenuSheet({ goToChat, isPinned, ref, isInChat }: Chat }; const actions: Array = compact([ - { + canUseFolders && { title: translate('TEXT_MOVE_TO_FOLDER'), iconName: 'folderPlus', onPress: () => handleAction(ChatAction.MOVE_TO_FOLDER), diff --git a/libs/mobile/shared/features/use-can-use-folders/.babelrc b/libs/mobile/shared/features/use-can-use-folders/.babelrc new file mode 100644 index 00000000..1ea870ea --- /dev/null +++ b/libs/mobile/shared/features/use-can-use-folders/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/mobile/shared/features/use-can-use-folders/eslint.config.cjs b/libs/mobile/shared/features/use-can-use-folders/eslint.config.cjs new file mode 100644 index 00000000..6f47c1a7 --- /dev/null +++ b/libs/mobile/shared/features/use-can-use-folders/eslint.config.cjs @@ -0,0 +1,12 @@ +const nx = require('@nx/eslint-plugin'); +const baseConfig = require('../../../../../eslint.config.cjs'); + +module.exports = [ + ...baseConfig, + ...nx.configs['flat/react'], + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + // Override or add rules here + rules: {}, + }, +]; diff --git a/libs/mobile/shared/features/use-can-use-folders/project.json b/libs/mobile/shared/features/use-can-use-folders/project.json new file mode 100644 index 00000000..688c2589 --- /dev/null +++ b/libs/mobile/shared/features/use-can-use-folders/project.json @@ -0,0 +1,9 @@ +{ + "name": "mobile/shared/features/use-can-use-folders", + "$schema": "../../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/mobile/shared/features/use-can-use-folders/src", + "projectType": "library", + "tags": ["app:mobile", "scope:shared", "type:features"], + "// targets": "to see all targets run: nx show project mobile/shared/features/use-can-use-folders --web", + "targets": {} +} diff --git a/libs/mobile/shared/features/use-can-use-folders/src/hook.ts b/libs/mobile/shared/features/use-can-use-folders/src/hook.ts new file mode 100644 index 00000000..55597022 --- /dev/null +++ b/libs/mobile/shared/features/use-can-use-folders/src/hook.ts @@ -0,0 +1,22 @@ +import { appConfigurationApi, authApi } from '@open-webui-react-native/shared/data-access/api'; +import { UserRole } from '@open-webui-react-native/shared/data-access/common'; + +export interface UseCanUseFoldersResult { + canUseFolders: boolean; + isLoading: boolean; +} + +export function useCanUseFolders(): UseCanUseFoldersResult { + const { data: config, isLoading: isConfigLoading } = appConfigurationApi.useGetAppConfiguration(); + const { data: profile, isLoading: isProfileLoading } = authApi.useGetProfile(); + + // NOTE: matches the web app's gate (Sidebar.svelte) and the backend's check_folders_permission, + // which guards every folders route (read and write alike) — the instance-wide toggle must be on, + // and a non-admin additionally needs the per-user permission, defaulting to allowed when unset. + const canUseFolders = Boolean( + config?.features.enableFolders && + (profile?.role === UserRole.ADMIN || (profile?.permissions.features.folders ?? true)), + ); + + return { canUseFolders, isLoading: isConfigLoading || isProfileLoading }; +} diff --git a/libs/mobile/shared/features/use-can-use-folders/src/index.ts b/libs/mobile/shared/features/use-can-use-folders/src/index.ts new file mode 100644 index 00000000..1146a6d5 --- /dev/null +++ b/libs/mobile/shared/features/use-can-use-folders/src/index.ts @@ -0,0 +1 @@ +export * from './hook'; diff --git a/libs/mobile/shared/features/use-can-use-folders/tsconfig.json b/libs/mobile/shared/features/use-can-use-folders/tsconfig.json new file mode 100644 index 00000000..ec74bfc3 --- /dev/null +++ b/libs/mobile/shared/features/use-can-use-folders/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ], + "extends": "../../../../../tsconfig.base.json" +} diff --git a/libs/mobile/shared/features/use-can-use-folders/tsconfig.lib.json b/libs/mobile/shared/features/use-can-use-folders/tsconfig.lib.json new file mode 100644 index 00000000..27f2b917 --- /dev/null +++ b/libs/mobile/shared/features/use-can-use-folders/tsconfig.lib.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../../dist/out-tsc", + "types": ["node", "@nx/react/typings/cssmodule.d.ts", "@nx/react/typings/image.d.ts"] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/libs/shared/data-access/api/src/lib/app-configuration/models/features.ts b/libs/shared/data-access/api/src/lib/app-configuration/models/features.ts index b84eb3e4..a0880e83 100644 --- a/libs/shared/data-access/api/src/lib/app-configuration/models/features.ts +++ b/libs/shared/data-access/api/src/lib/app-configuration/models/features.ts @@ -25,6 +25,9 @@ export class Features { @Expose({ name: 'enable_direct_connections' }) public enableDirectConnections: boolean; + @Expose({ name: 'enable_folders' }) + public enableFolders: boolean; + @Expose({ name: 'enable_channels' }) public enableChannels: boolean; diff --git a/libs/shared/data-access/api/src/lib/auth/models/permissions/features-permissions.ts b/libs/shared/data-access/api/src/lib/auth/models/permissions/features-permissions.ts index 57e5f207..ba2eba0c 100644 --- a/libs/shared/data-access/api/src/lib/auth/models/permissions/features-permissions.ts +++ b/libs/shared/data-access/api/src/lib/auth/models/permissions/features-permissions.ts @@ -16,6 +16,9 @@ export class FeaturesPermissions { @Expose() public notes: boolean; + @Expose() + public folders: boolean; + constructor(model: Partial) { Object.assign(this, model); } diff --git a/libs/shared/data-access/api/src/lib/folders/api.ts b/libs/shared/data-access/api/src/lib/folders/api.ts index ff036e2d..22fe17c3 100644 --- a/libs/shared/data-access/api/src/lib/folders/api.ts +++ b/libs/shared/data-access/api/src/lib/folders/api.ts @@ -122,7 +122,7 @@ function useDeleteFolder( } function useGetFolders( - props?: UseQueryOptions, AxiosError>, + props?: Omit, AxiosError>, 'queryKey' | 'queryFn'>, ): UseQueryResult, AxiosError> { return useQuery, AxiosError>({ queryFn: foldersService.getFolders, diff --git a/tsconfig.base.json b/tsconfig.base.json index 24470c31..08314060 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -212,6 +212,9 @@ "@open-webui-react-native/mobile/shared/features/use-audio-recorder": [ "libs/mobile/shared/features/use-audio-recorder/src/index.ts" ], + "@open-webui-react-native/mobile/shared/features/use-can-use-folders": [ + "libs/mobile/shared/features/use-can-use-folders/src/index.ts" + ], "@open-webui-react-native/mobile/shared/features/use-dictate-mode": [ "libs/mobile/shared/features/use-dictate-mode/src/index.ts" ], From a57183566f98f6dc9258add3a740a85d8b00d84a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D1=81=D0=B8=D0=BB=D0=B8=D0=B9=20=D0=95=D0=BB?= =?UTF-8?q?=D0=B8=D1=81=D0=B5=D0=B5=D0=B2?= Date: Tue, 15 Sep 2026 09:57:34 +0600 Subject: [PATCH 2/2] fix: minor fixes --- apps/mobile/app/(main)/chat/list.tsx | 7 ++----- .../components/search-folder-view/component.tsx | 4 ++-- .../chat/features/menu-list/src/lib/component.tsx | 4 ++-- .../folder-actions-sheet/src/lib/component.tsx | 4 ++-- .../chat-actions-menu-sheet/src/lib/component.tsx | 4 ++-- .../.babelrc | 0 .../eslint.config.cjs | 0 .../project.json | 6 +++--- .../src/hook.ts | 15 ++++----------- .../src/index.ts | 0 .../tsconfig.json | 0 .../tsconfig.lib.json | 0 tsconfig.base.json | 4 ++-- 13 files changed, 19 insertions(+), 29 deletions(-) rename libs/mobile/shared/features/{use-can-use-folders => use-folders-enabled}/.babelrc (100%) rename libs/mobile/shared/features/{use-can-use-folders => use-folders-enabled}/eslint.config.cjs (100%) rename libs/mobile/shared/features/{use-can-use-folders => use-folders-enabled}/project.json (60%) rename libs/mobile/shared/features/{use-can-use-folders => use-folders-enabled}/src/hook.ts (57%) rename libs/mobile/shared/features/{use-can-use-folders => use-folders-enabled}/src/index.ts (100%) rename libs/mobile/shared/features/{use-can-use-folders => use-folders-enabled}/tsconfig.json (100%) rename libs/mobile/shared/features/{use-can-use-folders => use-folders-enabled}/tsconfig.lib.json (100%) diff --git a/apps/mobile/app/(main)/chat/list.tsx b/apps/mobile/app/(main)/chat/list.tsx index 34e9b7c6..d6a6e8dd 100644 --- a/apps/mobile/app/(main)/chat/list.tsx +++ b/apps/mobile/app/(main)/chat/list.tsx @@ -11,7 +11,7 @@ import { UpsertFolderSheet, UpsertFolderSheetMethods, } from '@open-webui-react-native/mobile/folder/features/upsert-folder-sheet'; -import { useCanUseFolders } from '@open-webui-react-native/mobile/shared/features/use-can-use-folders'; +import { useFoldersEnabled } from '@open-webui-react-native/mobile/shared/features/use-folders-enabled'; import { ScreenWrapper } from '@open-webui-react-native/mobile/shared/ui/screen-wrapper'; import { AppHeader, AppPressable, Avatar, IconButton, View } from '@open-webui-react-native/mobile/shared/ui/ui-kit'; import { navigationConfig } from '@open-webui-react-native/mobile/shared/utils/navigation'; @@ -30,16 +30,13 @@ export default function ChatListScreen(): ReactElement { const upsertFolderSheetRef = useRef(null); const shareFolderSheetRef = useRef(null); const { data: profile } = authApi.useGetProfile(); - const { canUseFolders } = useCanUseFolders(); + const canUseFolders = useFoldersEnabled(); const handleChatPress = (id: string): void => navigateOnce(navigationConfig.main.chat.view({ id })); const handleNewChatPress = (): void => navigateOnce(`${navigationConfig.main.chat.index}/${navigationConfig.main.chat.create}`); - const handleArchivedChatsPress = (): void => - navigateOnce(`${navigationConfig.main.chat.index}/${navigationConfig.main.chat.archivedChats}`); - const handleSettingsPress = (): void => navigateOnce(navigationConfig.main.settings); const handleFolderPress = (id: string, title: string): void => diff --git a/libs/mobile/chat/features/create-chat/src/lib/components/search-folder-view/component.tsx b/libs/mobile/chat/features/create-chat/src/lib/components/search-folder-view/component.tsx index 17613abf..d3fa057c 100644 --- a/libs/mobile/chat/features/create-chat/src/lib/components/search-folder-view/component.tsx +++ b/libs/mobile/chat/features/create-chat/src/lib/components/search-folder-view/component.tsx @@ -4,7 +4,7 @@ import { FolderSearchItem, useFolderSearchList, } from '@open-webui-react-native/mobile/chat/utils/use-folder-search-list'; -import { useCanUseFolders } from '@open-webui-react-native/mobile/shared/features/use-can-use-folders'; +import { useFoldersEnabled } from '@open-webui-react-native/mobile/shared/features/use-folders-enabled'; import { useColorScheme } from '@open-webui-react-native/mobile/shared/ui/styles'; import { AppPressable, @@ -33,7 +33,7 @@ export function SearchFolderView({ const translate = useTranslation('CHAT.CREATE_CHAT.SEARCH_FOLDER_VIEW'); const { isDarkColorScheme } = useColorScheme(); - const { canUseFolders } = useCanUseFolders(); + const canUseFolders = useFoldersEnabled(); const { emptyFolders } = useFolderSearchList({ noFolderText: translate('TEXT_NO_FOLDER'), createFolderText: translate('TEXT_CREATE_NEW_FOLDER'), diff --git a/libs/mobile/chat/features/menu-list/src/lib/component.tsx b/libs/mobile/chat/features/menu-list/src/lib/component.tsx index ba79688c..1fe35e47 100644 --- a/libs/mobile/chat/features/menu-list/src/lib/component.tsx +++ b/libs/mobile/chat/features/menu-list/src/lib/component.tsx @@ -5,7 +5,7 @@ import { ChatActionsMenuSheet, ChatActionsMenuSheetMethods, } from '@open-webui-react-native/mobile/shared/features/chat-actions-menu-sheet'; -import { useCanUseFolders } from '@open-webui-react-native/mobile/shared/features/use-can-use-folders'; +import { useFoldersEnabled } from '@open-webui-react-native/mobile/shared/features/use-folders-enabled'; import { ChatListRow } from '@open-webui-react-native/mobile/shared/ui/chat-list-row'; import { DateSectionList } from '@open-webui-react-native/mobile/shared/ui/date-section-list'; import { @@ -45,7 +45,7 @@ export function ChatMenuList({ const [isFirstLoading, setIsFirstLoading] = useState(true); - const { canUseFolders } = useCanUseFolders(); + const canUseFolders = useFoldersEnabled(); const { data: chats, diff --git a/libs/mobile/folder/features/folder-actions-sheet/src/lib/component.tsx b/libs/mobile/folder/features/folder-actions-sheet/src/lib/component.tsx index 1d9656b2..3e051c6d 100644 --- a/libs/mobile/folder/features/folder-actions-sheet/src/lib/component.tsx +++ b/libs/mobile/folder/features/folder-actions-sheet/src/lib/component.tsx @@ -2,7 +2,7 @@ import { BottomSheetModal } from '@gorhom/bottom-sheet'; import { useTranslation } from '@ronas-it/react-native-common-modules/i18n'; import { ForwardedRef, ReactElement, useImperativeHandle, useRef, useState } from 'react'; import { fileSystemService } from '@open-webui-react-native/mobile/shared/data-access/file-system-service'; -import { useCanUseFolders } from '@open-webui-react-native/mobile/shared/features/use-can-use-folders'; +import { useFoldersEnabled } from '@open-webui-react-native/mobile/shared/features/use-folders-enabled'; import { ActionsBottomSheet, ActionsBottomSheetProps, @@ -40,7 +40,7 @@ export function FolderActionsSheet({ onEditPress, onSharePress, ref }: FolderAct const [isExportLoading, setIsExportLoading] = useState(false); const { data: profile } = authApi.useGetProfile(); - const { canUseFolders } = useCanUseFolders(); + const canUseFolders = useFoldersEnabled(); const { data: sharedFolders } = foldersApi.useGetSharedFolders({ enabled: canUseFolders }); const { mutateAsync: deleteFolder, isPending: isDeleting } = foldersApi.useDeleteFolder(); diff --git a/libs/mobile/shared/features/chat-actions-menu-sheet/src/lib/component.tsx b/libs/mobile/shared/features/chat-actions-menu-sheet/src/lib/component.tsx index 31c74821..953aed03 100644 --- a/libs/mobile/shared/features/chat-actions-menu-sheet/src/lib/component.tsx +++ b/libs/mobile/shared/features/chat-actions-menu-sheet/src/lib/component.tsx @@ -12,7 +12,7 @@ import { UpsertFolderSheetMethods, } from '@open-webui-react-native/mobile/folder/features/upsert-folder-sheet'; import { DownloadChatOptionsSheet } from '@open-webui-react-native/mobile/shared/features/download-chat-options-sheet'; -import { useCanUseFolders } from '@open-webui-react-native/mobile/shared/features/use-can-use-folders'; +import { useFoldersEnabled } from '@open-webui-react-native/mobile/shared/features/use-folders-enabled'; import { ActionButtonsModal, ActionButtonsModalMethods, @@ -65,7 +65,7 @@ export function ChatActionsMenuSheet({ goToChat, isPinned, ref, isInChat }: Chat const { mutateAsync: cloneChat, isPending: isCloning } = chatApi.useCloneChat(); const { mutateAsync: archiveChat, isPending: isArchiving } = chatApi.useArchiveChat(); const { mutateAsync: unarchiveChat, isPending: isUnarchiving } = chatApi.useUnarchiveChat(); - const { canUseFolders } = useCanUseFolders(); + const canUseFolders = useFoldersEnabled(); const { data: folders } = foldersApi.useGetFolders({ enabled: canUseFolders }); const [activeChat, setActiveChat] = useState(null); diff --git a/libs/mobile/shared/features/use-can-use-folders/.babelrc b/libs/mobile/shared/features/use-folders-enabled/.babelrc similarity index 100% rename from libs/mobile/shared/features/use-can-use-folders/.babelrc rename to libs/mobile/shared/features/use-folders-enabled/.babelrc diff --git a/libs/mobile/shared/features/use-can-use-folders/eslint.config.cjs b/libs/mobile/shared/features/use-folders-enabled/eslint.config.cjs similarity index 100% rename from libs/mobile/shared/features/use-can-use-folders/eslint.config.cjs rename to libs/mobile/shared/features/use-folders-enabled/eslint.config.cjs diff --git a/libs/mobile/shared/features/use-can-use-folders/project.json b/libs/mobile/shared/features/use-folders-enabled/project.json similarity index 60% rename from libs/mobile/shared/features/use-can-use-folders/project.json rename to libs/mobile/shared/features/use-folders-enabled/project.json index 688c2589..a00a162d 100644 --- a/libs/mobile/shared/features/use-can-use-folders/project.json +++ b/libs/mobile/shared/features/use-folders-enabled/project.json @@ -1,9 +1,9 @@ { - "name": "mobile/shared/features/use-can-use-folders", + "name": "mobile/shared/features/use-folders-enabled", "$schema": "../../../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "libs/mobile/shared/features/use-can-use-folders/src", + "sourceRoot": "libs/mobile/shared/features/use-folders-enabled/src", "projectType": "library", "tags": ["app:mobile", "scope:shared", "type:features"], - "// targets": "to see all targets run: nx show project mobile/shared/features/use-can-use-folders --web", + "// targets": "to see all targets run: nx show project mobile/shared/features/use-folders-enabled --web", "targets": {} } diff --git a/libs/mobile/shared/features/use-can-use-folders/src/hook.ts b/libs/mobile/shared/features/use-folders-enabled/src/hook.ts similarity index 57% rename from libs/mobile/shared/features/use-can-use-folders/src/hook.ts rename to libs/mobile/shared/features/use-folders-enabled/src/hook.ts index 55597022..388aa80d 100644 --- a/libs/mobile/shared/features/use-can-use-folders/src/hook.ts +++ b/libs/mobile/shared/features/use-folders-enabled/src/hook.ts @@ -1,22 +1,15 @@ import { appConfigurationApi, authApi } from '@open-webui-react-native/shared/data-access/api'; import { UserRole } from '@open-webui-react-native/shared/data-access/common'; -export interface UseCanUseFoldersResult { - canUseFolders: boolean; - isLoading: boolean; -} - -export function useCanUseFolders(): UseCanUseFoldersResult { - const { data: config, isLoading: isConfigLoading } = appConfigurationApi.useGetAppConfiguration(); - const { data: profile, isLoading: isProfileLoading } = authApi.useGetProfile(); +export function useFoldersEnabled(): boolean { + const { data: config } = appConfigurationApi.useGetAppConfiguration(); + const { data: profile } = authApi.useGetProfile(); // NOTE: matches the web app's gate (Sidebar.svelte) and the backend's check_folders_permission, // which guards every folders route (read and write alike) — the instance-wide toggle must be on, // and a non-admin additionally needs the per-user permission, defaulting to allowed when unset. - const canUseFolders = Boolean( + return Boolean( config?.features.enableFolders && (profile?.role === UserRole.ADMIN || (profile?.permissions.features.folders ?? true)), ); - - return { canUseFolders, isLoading: isConfigLoading || isProfileLoading }; } diff --git a/libs/mobile/shared/features/use-can-use-folders/src/index.ts b/libs/mobile/shared/features/use-folders-enabled/src/index.ts similarity index 100% rename from libs/mobile/shared/features/use-can-use-folders/src/index.ts rename to libs/mobile/shared/features/use-folders-enabled/src/index.ts diff --git a/libs/mobile/shared/features/use-can-use-folders/tsconfig.json b/libs/mobile/shared/features/use-folders-enabled/tsconfig.json similarity index 100% rename from libs/mobile/shared/features/use-can-use-folders/tsconfig.json rename to libs/mobile/shared/features/use-folders-enabled/tsconfig.json diff --git a/libs/mobile/shared/features/use-can-use-folders/tsconfig.lib.json b/libs/mobile/shared/features/use-folders-enabled/tsconfig.lib.json similarity index 100% rename from libs/mobile/shared/features/use-can-use-folders/tsconfig.lib.json rename to libs/mobile/shared/features/use-folders-enabled/tsconfig.lib.json diff --git a/tsconfig.base.json b/tsconfig.base.json index 08314060..ea272e1a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -212,8 +212,8 @@ "@open-webui-react-native/mobile/shared/features/use-audio-recorder": [ "libs/mobile/shared/features/use-audio-recorder/src/index.ts" ], - "@open-webui-react-native/mobile/shared/features/use-can-use-folders": [ - "libs/mobile/shared/features/use-can-use-folders/src/index.ts" + "@open-webui-react-native/mobile/shared/features/use-folders-enabled": [ + "libs/mobile/shared/features/use-folders-enabled/src/index.ts" ], "@open-webui-react-native/mobile/shared/features/use-dictate-mode": [ "libs/mobile/shared/features/use-dictate-mode/src/index.ts"