From 05d26bc02ca9cac5c3522fadac54141f2bff89b4 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Mon, 21 Jul 2025 11:27:25 +0800 Subject: [PATCH] remove applist in app context provider --- web/app/(commonLayout)/layout.tsx | 6 +++--- web/app/account/account-page/index.tsx | 9 ++++++-- web/app/account/layout.tsx | 2 +- web/app/components/app-sidebar/app-info.tsx | 16 ++++---------- .../components/app/create-app-modal/index.tsx | 8 +++---- web/app/components/apps/app-card.tsx | 21 +++++-------------- ...ser-initor.tsx => browser-initializer.tsx} | 6 +++--- ...ntry-initor.tsx => sentry-initializer.tsx} | 6 +++--- .../{swr-initor.tsx => swr-initializer.tsx} | 8 +++---- web/app/layout.tsx | 18 ++++++++-------- web/context/app-context.tsx | 14 ++----------- web/context/query-client.tsx | 2 +- 12 files changed, 45 insertions(+), 71 deletions(-) rename web/app/components/{browser-initor.tsx => browser-initializer.tsx} (88%) rename web/app/components/{sentry-initor.tsx => sentry-initializer.tsx} (85%) rename web/app/components/{swr-initor.tsx => swr-initializer.tsx} (95%) diff --git a/web/app/(commonLayout)/layout.tsx b/web/app/(commonLayout)/layout.tsx index d07e2a99d9..64186a1b10 100644 --- a/web/app/(commonLayout)/layout.tsx +++ b/web/app/(commonLayout)/layout.tsx @@ -1,6 +1,6 @@ import React from 'react' import type { ReactNode } from 'react' -import SwrInitor from '@/app/components/swr-initor' +import SwrInitializer from '@/app/components/swr-initializer' import { AppContextProvider } from '@/context/app-context' import GA, { GaType } from '@/app/components/base/ga' import HeaderWrapper from '@/app/components/header/header-wrapper' @@ -13,7 +13,7 @@ const Layout = ({ children }: { children: ReactNode }) => { return ( <> - + @@ -26,7 +26,7 @@ const Layout = ({ children }: { children: ReactNode }) => { - + ) } diff --git a/web/app/account/account-page/index.tsx b/web/app/account/account-page/index.tsx index 55fa2983dd..47b8f045d2 100644 --- a/web/app/account/account-page/index.tsx +++ b/web/app/account/account-page/index.tsx @@ -1,5 +1,6 @@ 'use client' import { useState } from 'react' +import useSWR from 'swr' import { useTranslation } from 'react-i18next' import { RiGraduationCapFill, @@ -22,6 +23,8 @@ import PremiumBadge from '@/app/components/base/premium-badge' import { useGlobalPublicStore } from '@/context/global-public-context' import EmailChangeModal from './email-change-modal' import { validPassword } from '@/config' +import { fetchAppList } from '@/service/apps' +import type { App } from '@/types/app' const titleClassName = ` system-sm-semibold text-text-secondary @@ -33,7 +36,9 @@ const descriptionClassName = ` export default function AccountPage() { const { t } = useTranslation() const { systemFeatures } = useGlobalPublicStore() - const { mutateUserProfile, userProfile, apps } = useAppContext() + const { data: appList } = useSWR({ url: '/apps', params: { page: 1, limit: 100, name: '' } }, fetchAppList) + const apps = appList?.data || [] + const { mutateUserProfile, userProfile } = useAppContext() const { isEducationAccount } = useProviderContext() const { notify } = useContext(ToastContext) const [editNameModalVisible, setEditNameModalVisible] = useState(false) @@ -202,7 +207,7 @@ export default function AccountPage() { {!!apps.length && ( ({ ...app, key: app.id, name: app.name }))} + items={apps.map((app: App) => ({ ...app, key: app.id, name: app.name }))} renderItem={renderAppItem} wrapperClassName='mt-2' /> diff --git a/web/app/account/layout.tsx b/web/app/account/layout.tsx index e74716fb3b..b3225b5341 100644 --- a/web/app/account/layout.tsx +++ b/web/app/account/layout.tsx @@ -1,7 +1,7 @@ import React from 'react' import type { ReactNode } from 'react' import Header from './header' -import SwrInitor from '@/app/components/swr-initor' +import SwrInitor from '@/app/components/swr-initializer' import { AppContextProvider } from '@/context/app-context' import GA, { GaType } from '@/app/components/base/ga' import HeaderWrapper from '@/app/components/header/header-wrapper' diff --git a/web/app/components/app-sidebar/app-info.tsx b/web/app/components/app-sidebar/app-info.tsx index e85eaa2f53..c35047bbc5 100644 --- a/web/app/components/app-sidebar/app-info.tsx +++ b/web/app/components/app-sidebar/app-info.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next' import { useRouter } from 'next/navigation' -import { useContext, useContextSelector } from 'use-context-selector' +import { useContext } from 'use-context-selector' import React, { useCallback, useState } from 'react' import { RiDeleteBinLine, @@ -15,7 +15,7 @@ import AppIcon from '../base/app-icon' import cn from '@/utils/classnames' import { useStore as useAppStore } from '@/app/components/app/store' import { ToastContext } from '@/app/components/base/toast' -import AppsContext, { useAppContext } from '@/context/app-context' +import { useAppContext } from '@/context/app-context' import { useProviderContext } from '@/context/provider-context' import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/apps' import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal' @@ -73,11 +73,6 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx const [showImportDSLModal, setShowImportDSLModal] = useState(false) const [secretEnvList, setSecretEnvList] = useState([]) - const mutateApps = useContextSelector( - AppsContext, - state => state.mutateApps, - ) - const onEdit: CreateAppModalProps['onConfirm'] = useCallback(async ({ name, icon_type, @@ -106,12 +101,11 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx message: t('app.editDone'), }) setAppDetail(app) - mutateApps() } catch { notify({ type: 'error', message: t('app.editFailed') }) } - }, [appDetail, mutateApps, notify, setAppDetail, t]) + }, [appDetail, notify, setAppDetail, t]) const onCopy: DuplicateAppModalProps['onConfirm'] = async ({ name, icon_type, icon, icon_background }) => { if (!appDetail) @@ -131,7 +125,6 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx message: t('app.newApp.appCreated'), }) localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') - mutateApps() onPlanInfoChanged() getRedirection(true, newApp, replace) } @@ -186,7 +179,6 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx try { await deleteApp(appDetail.id) notify({ type: 'success', message: t('app.appDeleted') }) - mutateApps() onPlanInfoChanged() setAppDetail() replace('/apps') @@ -198,7 +190,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx }) } setShowConfirmDelete(false) - }, [appDetail, mutateApps, notify, onPlanInfoChanged, replace, setAppDetail, t]) + }, [appDetail, notify, onPlanInfoChanged, replace, setAppDetail, t]) const { isCurrentWorkspaceEditor } = useAppContext() diff --git a/web/app/components/app/create-app-modal/index.tsx b/web/app/components/app/create-app-modal/index.tsx index f0a0da41a5..bdc839e848 100644 --- a/web/app/components/app/create-app-modal/index.tsx +++ b/web/app/components/app/create-app-modal/index.tsx @@ -4,7 +4,7 @@ import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useRouter } from 'next/navigation' -import { useContext, useContextSelector } from 'use-context-selector' +import { useContext } from 'use-context-selector' import { RiArrowRightLine, RiArrowRightSLine, RiCommandLine, RiCornerDownLeftLine, RiExchange2Fill } from '@remixicon/react' import Link from 'next/link' import { useDebounceFn, useKeyPress } from 'ahooks' @@ -15,7 +15,7 @@ import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' import cn from '@/utils/classnames' import { basePath } from '@/utils/var' -import AppsContext, { useAppContext } from '@/context/app-context' +import { useAppContext } from '@/context/app-context' import { useProviderContext } from '@/context/provider-context' import { ToastContext } from '@/app/components/base/toast' import type { AppMode } from '@/types/app' @@ -41,7 +41,6 @@ function CreateApp({ onClose, onSuccess, onCreateFromTemplate }: CreateAppProps) const { t } = useTranslation() const { push } = useRouter() const { notify } = useContext(ToastContext) - const mutateApps = useContextSelector(AppsContext, state => state.mutateApps) const [appMode, setAppMode] = useState('advanced-chat') const [appIcon, setAppIcon] = useState({ type: 'emoji', icon: '🤖', background: '#FFEAD5' }) @@ -80,7 +79,6 @@ function CreateApp({ onClose, onSuccess, onCreateFromTemplate }: CreateAppProps) notify({ type: 'success', message: t('app.newApp.appCreated') }) onSuccess() onClose() - mutateApps() localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') getRedirection(isCurrentWorkspaceEditor, app, push) } @@ -88,7 +86,7 @@ function CreateApp({ onClose, onSuccess, onCreateFromTemplate }: CreateAppProps) notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) } isCreatingRef.current = false - }, [name, notify, t, appMode, appIcon, description, onSuccess, onClose, mutateApps, push, isCurrentWorkspaceEditor]) + }, [name, notify, t, appMode, appIcon, description, onSuccess, onClose, push, isCurrentWorkspaceEditor]) const { run: handleCreateApp } = useDebounceFn(onCreate, { wait: 300 }) useKeyPress(['meta.enter', 'ctrl.enter'], () => { diff --git a/web/app/components/apps/app-card.tsx b/web/app/components/apps/app-card.tsx index bfb7813bf4..603b5922c5 100644 --- a/web/app/components/apps/app-card.tsx +++ b/web/app/components/apps/app-card.tsx @@ -1,7 +1,7 @@ 'use client' import React, { useCallback, useEffect, useMemo, useState } from 'react' -import { useContext, useContextSelector } from 'use-context-selector' +import { useContext } from 'use-context-selector' import { useRouter } from 'next/navigation' import { useTranslation } from 'react-i18next' import { RiBuildingLine, RiGlobalLine, RiLockLine, RiMoreFill, RiVerifiedBadgeLine } from '@remixicon/react' @@ -11,7 +11,7 @@ import Toast, { ToastContext } from '@/app/components/base/toast' import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/apps' import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal' import AppIcon from '@/app/components/base/app-icon' -import AppsContext, { useAppContext } from '@/context/app-context' +import { useAppContext } from '@/context/app-context' import type { HtmlContentProps } from '@/app/components/base/popover' import CustomPopover from '@/app/components/base/popover' import Divider from '@/app/components/base/divider' @@ -65,11 +65,6 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { const { onPlanInfoChanged } = useProviderContext() const { push } = useRouter() - const mutateApps = useContextSelector( - AppsContext, - state => state.mutateApps, - ) - const [showEditModal, setShowEditModal] = useState(false) const [showDuplicateModal, setShowDuplicateModal] = useState(false) const [showSwitchModal, setShowSwitchModal] = useState(false) @@ -83,7 +78,6 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { notify({ type: 'success', message: t('app.appDeleted') }) if (onRefresh) onRefresh() - mutateApps() onPlanInfoChanged() } catch (e: any) { @@ -93,7 +87,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { }) } setShowConfirmDelete(false) - }, [app.id, mutateApps, notify, onPlanInfoChanged, onRefresh, t]) + }, [app.id, notify, onPlanInfoChanged, onRefresh, t]) const onEdit: CreateAppModalProps['onConfirm'] = useCallback(async ({ name, @@ -122,12 +116,11 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { }) if (onRefresh) onRefresh() - mutateApps() } catch { notify({ type: 'error', message: t('app.editFailed') }) } - }, [app.id, mutateApps, notify, onRefresh, t]) + }, [app.id, notify, onRefresh, t]) const onCopy: DuplicateAppModalProps['onConfirm'] = async ({ name, icon_type, icon, icon_background }) => { try { @@ -147,7 +140,6 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') if (onRefresh) onRefresh() - mutateApps() onPlanInfoChanged() getRedirection(isCurrentWorkspaceEditor, newApp, push) } @@ -195,16 +187,14 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { const onSwitch = () => { if (onRefresh) onRefresh() - mutateApps() setShowSwitchModal(false) } const onUpdateAccessControl = useCallback(() => { if (onRefresh) onRefresh() - mutateApps() setShowAccessControl(false) - }, [onRefresh, mutateApps, setShowAccessControl]) + }, [onRefresh, setShowAccessControl]) const Operations = (props: HtmlContentProps) => { const { data: userCanAccessApp, isLoading: isGettingUserCanAccessApp } = useGetUserCanAccessApp({ appId: app?.id, enabled: (!!props?.open && systemFeatures.webapp_auth.enabled) }) @@ -325,7 +315,6 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { dateFormat: `${t('datasetDocuments.segment.dateTimeFormat')}`, }) return `${t('datasetDocuments.segment.editedAt')} ${timeText}` - // eslint-disable-next-line react-hooks/exhaustive-deps }, [app.updated_at, app.created_at]) return ( diff --git a/web/app/components/browser-initor.tsx b/web/app/components/browser-initializer.tsx similarity index 88% rename from web/app/components/browser-initor.tsx rename to web/app/components/browser-initializer.tsx index f2f4b02dc0..fcae22c448 100644 --- a/web/app/components/browser-initor.tsx +++ b/web/app/components/browser-initializer.tsx @@ -43,10 +43,10 @@ Object.defineProperty(globalThis, 'sessionStorage', { value: sessionStorage, }) -const BrowserInitor = ({ +const BrowserInitializer = ({ children, -}: { children: React.ReactNode }) => { +}: { children: React.ReactElement }) => { return children } -export default BrowserInitor +export default BrowserInitializer diff --git a/web/app/components/sentry-initor.tsx b/web/app/components/sentry-initializer.tsx similarity index 85% rename from web/app/components/sentry-initor.tsx rename to web/app/components/sentry-initializer.tsx index 457a1cf7c7..10c056f21b 100644 --- a/web/app/components/sentry-initor.tsx +++ b/web/app/components/sentry-initializer.tsx @@ -5,9 +5,9 @@ import * as Sentry from '@sentry/react' const isDevelopment = process.env.NODE_ENV === 'development' -const SentryInit = ({ +const SentryInitializer = ({ children, -}: { children: React.ReactNode }) => { +}: { children: React.ReactElement }) => { useEffect(() => { const SENTRY_DSN = document?.body?.getAttribute('data-public-sentry-dsn') if (!isDevelopment && SENTRY_DSN) { @@ -26,4 +26,4 @@ const SentryInit = ({ return children } -export default SentryInit +export default SentryInitializer diff --git a/web/app/components/swr-initor.tsx b/web/app/components/swr-initializer.tsx similarity index 95% rename from web/app/components/swr-initor.tsx rename to web/app/components/swr-initializer.tsx index 8f9c5b4e05..3592a0e017 100644 --- a/web/app/components/swr-initor.tsx +++ b/web/app/components/swr-initializer.tsx @@ -10,12 +10,12 @@ import { EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION, } from '@/app/education-apply/constants' -type SwrInitorProps = { +type SwrInitializerProps = { children: ReactNode } -const SwrInitor = ({ +const SwrInitializer = ({ children, -}: SwrInitorProps) => { +}: SwrInitializerProps) => { const router = useRouter() const searchParams = useSearchParams() const consoleToken = decodeURIComponent(searchParams.get('access_token') || '') @@ -86,4 +86,4 @@ const SwrInitor = ({ : null } -export default SwrInitor +export default SwrInitializer diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 525445db30..f086499ca4 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -1,10 +1,10 @@ import RoutePrefixHandle from './routePrefixHandle' import type { Viewport } from 'next' import I18nServer from './components/i18n-server' -import BrowserInitor from './components/browser-initor' -import SentryInitor from './components/sentry-initor' +import BrowserInitializer from './components/browser-initializer' +import SentryInitializer from './components/sentry-initializer' import { getLocaleOnServer } from '@/i18n/server' -import { TanstackQueryIniter } from '@/context/query-client' +import { TanstackQueryInitializer } from '@/context/query-client' import { ThemeProvider } from 'next-themes' import './styles/globals.css' import './styles/markdown.scss' @@ -62,9 +62,9 @@ const LocaleLayout = async ({ className="color-scheme h-full select-auto" {...datasetMap} > - - - + + + - - - + + + diff --git a/web/context/app-context.tsx b/web/context/app-context.tsx index 9b95b0f1eb..efb0251655 100644 --- a/web/context/app-context.tsx +++ b/web/context/app-context.tsx @@ -4,17 +4,13 @@ import { createRef, useCallback, useEffect, useMemo, useRef, useState } from 're import useSWR from 'swr' import { createContext, useContext, useContextSelector } from 'use-context-selector' import type { FC, ReactNode } from 'react' -import { fetchAppList } from '@/service/apps' import Loading from '@/app/components/base/loading' import { fetchCurrentWorkspace, fetchLanggeniusVersion, fetchUserProfile } from '@/service/common' -import type { App } from '@/types/app' import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common' import MaintenanceNotice from '@/app/components/header/maintenance-notice' import { noop } from 'lodash-es' export type AppContextValue = { - apps: App[] - mutateApps: VoidFunction userProfile: UserProfileResponse mutateUserProfile: VoidFunction currentWorkspace: ICurrentWorkspace @@ -23,7 +19,7 @@ export type AppContextValue = { isCurrentWorkspaceEditor: boolean isCurrentWorkspaceDatasetOperator: boolean mutateCurrentWorkspace: VoidFunction - pageContainerRef: React.RefObject + pageContainerRef: React.RefObject langeniusVersionInfo: LangGeniusVersionResponse useSelector: typeof useSelector isLoadingCurrentWorkspace: boolean @@ -50,8 +46,6 @@ const initialWorkspaceInfo: ICurrentWorkspace = { } const AppContext = createContext({ - apps: [], - mutateApps: noop, userProfile: { id: '', name: '', @@ -83,8 +77,6 @@ export type AppContextProviderProps = { export const AppContextProvider: FC = ({ children }) => { const pageContainerRef = useRef(null) - - const { data: appList, mutate: mutateApps } = useSWR({ url: '/apps', params: { page: 1, limit: 30, name: '' } }, fetchAppList) const { data: userProfileResponse, mutate: mutateUserProfile } = useSWR({ url: '/account/profile', params: {} }, fetchUserProfile) const { data: currentWorkspaceResponse, mutate: mutateCurrentWorkspace, isLoading: isLoadingCurrentWorkspace } = useSWR({ url: '/workspaces/current', params: {} }, fetchCurrentWorkspace) @@ -115,13 +107,11 @@ export const AppContextProvider: FC = ({ children }) => setCurrentWorkspace(currentWorkspaceResponse) }, [currentWorkspaceResponse]) - if (!appList || !userProfile) + if (!userProfile) return return ( = (props) => { +export const TanstackQueryInitializer: FC = (props) => { const { children } = props return {children}