| @@ -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 ( | |||
| <> | |||
| <GA gaType={GaType.admin} /> | |||
| <SwrInitor> | |||
| <SwrInitializer> | |||
| <AppContextProvider> | |||
| <EventEmitterContextProvider> | |||
| <ProviderContextProvider> | |||
| @@ -26,7 +26,7 @@ const Layout = ({ children }: { children: ReactNode }) => { | |||
| </ProviderContextProvider> | |||
| </EventEmitterContextProvider> | |||
| </AppContextProvider> | |||
| </SwrInitor> | |||
| </SwrInitializer> | |||
| </> | |||
| ) | |||
| } | |||
| @@ -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 && ( | |||
| <Collapse | |||
| title={`${t('common.account.showAppLength', { length: apps.length })}`} | |||
| items={apps.map(app => ({ ...app, key: app.id, name: app.name }))} | |||
| items={apps.map((app: App) => ({ ...app, key: app.id, name: app.name }))} | |||
| renderItem={renderAppItem} | |||
| wrapperClassName='mt-2' | |||
| /> | |||
| @@ -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' | |||
| @@ -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<boolean>(false) | |||
| const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([]) | |||
| 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() | |||
| @@ -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<AppMode>('advanced-chat') | |||
| const [appIcon, setAppIcon] = useState<AppIconSelection>({ 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'], () => { | |||
| @@ -90,10 +90,10 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam | |||
| const [option, setOption] = useState<Option>('iframe') | |||
| const [isCopied, setIsCopied] = useState<OptionStatus>({ iframe: false, scripts: false, chromePlugin: false }) | |||
| const { langeniusVersionInfo } = useAppContext() | |||
| const { langGeniusVersionInfo } = useAppContext() | |||
| const themeBuilder = useThemeContext() | |||
| themeBuilder.buildTheme(siteInfo?.chat_color_theme ?? null, siteInfo?.chat_color_theme_inverted ?? false) | |||
| const isTestEnv = langeniusVersionInfo.current_env === 'TESTING' || langeniusVersionInfo.current_env === 'DEVELOPMENT' | |||
| const isTestEnv = langGeniusVersionInfo.current_env === 'TESTING' || langGeniusVersionInfo.current_env === 'DEVELOPMENT' | |||
| const onClickCopy = () => { | |||
| if (option === 'chromePlugin') { | |||
| const splitUrl = OPTION_MAP[option].getContent(appBaseUrl, accessToken).split(': ') | |||
| @@ -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<boolean>(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 ( | |||
| @@ -45,7 +45,7 @@ const Avatar = ({ | |||
| className={cn(textClassName, 'scale-[0.4] text-center text-white')} | |||
| style={style} | |||
| > | |||
| {name[0].toLocaleUpperCase()} | |||
| {name && name[0].toLocaleUpperCase()} | |||
| </div> | |||
| </div> | |||
| ) | |||
| @@ -21,7 +21,7 @@ const AppsFull: FC<{ loc: string; className?: string; }> = ({ | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| const { plan } = useProviderContext() | |||
| const { userProfile, langeniusVersionInfo } = useAppContext() | |||
| const { userProfile, langGeniusVersionInfo } = useAppContext() | |||
| const isTeam = plan.type === Plan.team | |||
| const usage = plan.usage.buildApps | |||
| const total = plan.total.buildApps | |||
| @@ -62,7 +62,7 @@ const AppsFull: FC<{ loc: string; className?: string; }> = ({ | |||
| )} | |||
| {plan.type !== Plan.sandbox && plan.type !== Plan.professional && ( | |||
| <Button variant='secondary-accent'> | |||
| <a target='_blank' rel='noopener noreferrer' href={mailToSupport(userProfile.email, plan.type, langeniusVersionInfo.current_version)}> | |||
| <a target='_blank' rel='noopener noreferrer' href={mailToSupport(userProfile.email, plan.type, langGeniusVersionInfo.current_version)}> | |||
| {t('billing.apps.contactUs')} | |||
| </a> | |||
| </Button> | |||
| @@ -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 | |||
| @@ -12,16 +12,16 @@ import { noop } from 'lodash-es' | |||
| import { useGlobalPublicStore } from '@/context/global-public-context' | |||
| type IAccountSettingProps = { | |||
| langeniusVersionInfo: LangGeniusVersionResponse | |||
| langGeniusVersionInfo: LangGeniusVersionResponse | |||
| onCancel: () => void | |||
| } | |||
| export default function AccountAbout({ | |||
| langeniusVersionInfo, | |||
| langGeniusVersionInfo, | |||
| onCancel, | |||
| }: IAccountSettingProps) { | |||
| const { t } = useTranslation() | |||
| const isLatest = langeniusVersionInfo.current_version === langeniusVersionInfo.latest_version | |||
| const isLatest = langGeniusVersionInfo.current_version === langGeniusVersionInfo.latest_version | |||
| const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) | |||
| return ( | |||
| @@ -43,7 +43,7 @@ export default function AccountAbout({ | |||
| /> | |||
| : <DifyLogo size='large' className='mx-auto' />} | |||
| <div className='text-center text-xs font-normal text-text-tertiary'>Version {langeniusVersionInfo?.current_version}</div> | |||
| <div className='text-center text-xs font-normal text-text-tertiary'>Version {langGeniusVersionInfo?.current_version}</div> | |||
| <div className='flex flex-col items-center gap-2 text-center text-xs font-normal text-text-secondary'> | |||
| <div>© {dayjs().year()} LangGenius, Inc., Contributors.</div> | |||
| <div className='text-text-accent'> | |||
| @@ -63,8 +63,8 @@ export default function AccountAbout({ | |||
| <div className='text-xs font-medium text-text-tertiary'> | |||
| { | |||
| isLatest | |||
| ? t('common.about.latestAvailable', { version: langeniusVersionInfo.latest_version }) | |||
| : t('common.about.nowAvailable', { version: langeniusVersionInfo.latest_version }) | |||
| ? t('common.about.latestAvailable', { version: langGeniusVersionInfo.latest_version }) | |||
| : t('common.about.nowAvailable', { version: langGeniusVersionInfo.latest_version }) | |||
| } | |||
| </div> | |||
| <div className='flex items-center'> | |||
| @@ -80,7 +80,7 @@ export default function AccountAbout({ | |||
| !isLatest && !IS_CE_EDITION && ( | |||
| <Button variant='primary' size='small'> | |||
| <Link | |||
| href={langeniusVersionInfo.release_notes} | |||
| href={langGeniusVersionInfo.release_notes} | |||
| target='_blank' rel='noopener noreferrer' | |||
| > | |||
| {t('common.about.updateNow')} | |||
| @@ -45,7 +45,7 @@ export default function AppSelector() { | |||
| const { t } = useTranslation() | |||
| const docLink = useDocLink() | |||
| const { userProfile, langeniusVersionInfo, isCurrentWorkspaceOwner } = useAppContext() | |||
| const { userProfile, langGeniusVersionInfo, isCurrentWorkspaceOwner } = useAppContext() | |||
| const { isEducationAccount } = useProviderContext() | |||
| const { setShowAccountSettingModal } = useModalContext() | |||
| @@ -180,8 +180,8 @@ export default function AppSelector() { | |||
| <RiInformation2Line className='size-4 shrink-0 text-text-tertiary' /> | |||
| <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.about')}</div> | |||
| <div className='flex shrink-0 items-center'> | |||
| <div className='system-xs-regular mr-2 text-text-tertiary'>{langeniusVersionInfo.current_version}</div> | |||
| <Indicator color={langeniusVersionInfo.current_version === langeniusVersionInfo.latest_version ? 'green' : 'orange'} /> | |||
| <div className='system-xs-regular mr-2 text-text-tertiary'>{langGeniusVersionInfo.current_version}</div> | |||
| <Indicator color={langGeniusVersionInfo.current_version === langGeniusVersionInfo.latest_version ? 'green' : 'orange'} /> | |||
| </div> | |||
| </div> | |||
| </MenuItem> | |||
| @@ -217,7 +217,7 @@ export default function AppSelector() { | |||
| } | |||
| </Menu> | |||
| { | |||
| aboutVisible && <AccountAbout onCancel={() => setAboutVisible(false)} langeniusVersionInfo={langeniusVersionInfo} /> | |||
| aboutVisible && <AccountAbout onCancel={() => setAboutVisible(false)} langGeniusVersionInfo={langGeniusVersionInfo} /> | |||
| } | |||
| </div > | |||
| ) | |||
| @@ -16,7 +16,7 @@ export default function Support() { | |||
| ` | |||
| const { t } = useTranslation() | |||
| const { plan } = useProviderContext() | |||
| const { userProfile, langeniusVersionInfo } = useAppContext() | |||
| const { userProfile, langGeniusVersionInfo } = useAppContext() | |||
| const canEmailSupport = plan.type === Plan.professional || plan.type === Plan.team || plan.type === Plan.enterprise | |||
| return <Menu as="div" className="relative h-full w-full"> | |||
| @@ -53,7 +53,7 @@ export default function Support() { | |||
| className={cn(itemClassName, 'group justify-between', | |||
| 'data-[active]:bg-state-base-hover', | |||
| )} | |||
| href={mailToSupport(userProfile.email, plan.type, langeniusVersionInfo.current_version)} | |||
| href={mailToSupport(userProfile.email, plan.type, langGeniusVersionInfo.current_version)} | |||
| target='_blank' rel='noopener noreferrer'> | |||
| <RiMailSendLine className='size-4 shrink-0 text-text-tertiary' /> | |||
| <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.emailSupport')}</div> | |||
| @@ -12,8 +12,8 @@ const headerEnvClassName: { [k: string]: string } = { | |||
| const EnvNav = () => { | |||
| const { t } = useTranslation() | |||
| const { langeniusVersionInfo } = useAppContext() | |||
| const showEnvTag = langeniusVersionInfo.current_env === 'TESTING' || langeniusVersionInfo.current_env === 'DEVELOPMENT' | |||
| const { langGeniusVersionInfo } = useAppContext() | |||
| const showEnvTag = langGeniusVersionInfo.current_env === 'TESTING' || langGeniusVersionInfo.current_env === 'DEVELOPMENT' | |||
| if (!showEnvTag) | |||
| return null | |||
| @@ -21,10 +21,10 @@ const EnvNav = () => { | |||
| return ( | |||
| <div className={` | |||
| mr-1 flex h-[22px] items-center rounded-md border px-2 text-xs font-medium | |||
| ${headerEnvClassName[langeniusVersionInfo.current_env]} | |||
| ${headerEnvClassName[langGeniusVersionInfo.current_env]} | |||
| `}> | |||
| { | |||
| langeniusVersionInfo.current_env === 'TESTING' && ( | |||
| langGeniusVersionInfo.current_env === 'TESTING' && ( | |||
| <> | |||
| <Beaker02 className='h-3 w-3' /> | |||
| <div className='ml-1 max-[1280px]:hidden'>{t('common.environment.testing')}</div> | |||
| @@ -32,7 +32,7 @@ const EnvNav = () => { | |||
| ) | |||
| } | |||
| { | |||
| langeniusVersionInfo.current_env === 'DEVELOPMENT' && ( | |||
| langGeniusVersionInfo.current_env === 'DEVELOPMENT' && ( | |||
| <> | |||
| <TerminalSquare className='h-3 w-3' /> | |||
| <div className='ml-1 max-[1280px]:hidden'>{t('common.environment.development')}</div> | |||
| @@ -48,7 +48,6 @@ const Installed: FC<Props> = ({ | |||
| useEffect(() => { | |||
| if (hasInstalled && uniqueIdentifier === installedInfoPayload.uniqueIdentifier) | |||
| onInstalled() | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, [hasInstalled]) | |||
| const [isInstalling, setIsInstalling] = React.useState(false) | |||
| @@ -105,12 +104,12 @@ const Installed: FC<Props> = ({ | |||
| } | |||
| } | |||
| const { langeniusVersionInfo } = useAppContext() | |||
| const { langGeniusVersionInfo } = useAppContext() | |||
| const isDifyVersionCompatible = useMemo(() => { | |||
| if (!langeniusVersionInfo.current_version) | |||
| if (!langGeniusVersionInfo.current_version) | |||
| return true | |||
| return gte(langeniusVersionInfo.current_version, payload.meta.minimum_dify_version ?? '0.0.0') | |||
| }, [langeniusVersionInfo.current_version, payload.meta.minimum_dify_version]) | |||
| return gte(langGeniusVersionInfo.current_version, payload.meta.minimum_dify_version ?? '0.0.0') | |||
| }, [langGeniusVersionInfo.current_version, payload.meta.minimum_dify_version]) | |||
| return ( | |||
| <> | |||
| @@ -59,7 +59,6 @@ const Installed: FC<Props> = ({ | |||
| useEffect(() => { | |||
| if (hasInstalled && uniqueIdentifier === installedInfoPayload.uniqueIdentifier) | |||
| onInstalled() | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, [hasInstalled]) | |||
| const handleCancel = () => { | |||
| @@ -120,12 +119,12 @@ const Installed: FC<Props> = ({ | |||
| } | |||
| } | |||
| const { langeniusVersionInfo } = useAppContext() | |||
| const { langGeniusVersionInfo } = useAppContext() | |||
| const { data: pluginDeclaration } = usePluginDeclarationFromMarketPlace(uniqueIdentifier) | |||
| const isDifyVersionCompatible = useMemo(() => { | |||
| if (!pluginDeclaration || !langeniusVersionInfo.current_version) return true | |||
| return gte(langeniusVersionInfo.current_version, pluginDeclaration?.manifest.meta.minimum_dify_version ?? '0.0.0') | |||
| }, [langeniusVersionInfo.current_version, pluginDeclaration]) | |||
| if (!pluginDeclaration || !langGeniusVersionInfo.current_version) return true | |||
| return gte(langGeniusVersionInfo.current_version, pluginDeclaration?.manifest.meta.minimum_dify_version ?? '0.0.0') | |||
| }, [langGeniusVersionInfo.current_version, pluginDeclaration]) | |||
| const { canInstall } = useInstallPluginLimit({ ...payload, from: 'marketplace' }) | |||
| return ( | |||
| @@ -62,13 +62,13 @@ const PluginItem: FC<Props> = ({ | |||
| return [PluginSource.github, PluginSource.marketplace].includes(source) ? author : '' | |||
| }, [source, author]) | |||
| const { langeniusVersionInfo } = useAppContext() | |||
| const { langGeniusVersionInfo } = useAppContext() | |||
| const isDifyVersionCompatible = useMemo(() => { | |||
| if (!langeniusVersionInfo.current_version) | |||
| if (!langGeniusVersionInfo.current_version) | |||
| return true | |||
| return gte(langeniusVersionInfo.current_version, declarationMeta.minimum_dify_version ?? '0.0.0') | |||
| }, [declarationMeta.minimum_dify_version, langeniusVersionInfo.current_version]) | |||
| return gte(langGeniusVersionInfo.current_version, declarationMeta.minimum_dify_version ?? '0.0.0') | |||
| }, [declarationMeta.minimum_dify_version, langGeniusVersionInfo.current_version]) | |||
| const handleDelete = () => { | |||
| refreshPluginList({ category } as any) | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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} | |||
| > | |||
| <BrowserInitor> | |||
| <SentryInitor> | |||
| <TanstackQueryIniter> | |||
| <BrowserInitializer> | |||
| <SentryInitializer> | |||
| <TanstackQueryInitializer> | |||
| <ThemeProvider | |||
| attribute='data-theme' | |||
| defaultTheme='system' | |||
| @@ -77,9 +77,9 @@ const LocaleLayout = async ({ | |||
| </GlobalPublicStoreProvider> | |||
| </I18nServer> | |||
| </ThemeProvider> | |||
| </TanstackQueryIniter> | |||
| </SentryInitor> | |||
| </BrowserInitor> | |||
| </TanstackQueryInitializer> | |||
| </SentryInitializer> | |||
| </BrowserInitializer> | |||
| <RoutePrefixHandle /> | |||
| </body> | |||
| </html> | |||
| @@ -1,20 +1,15 @@ | |||
| 'use client' | |||
| import { createRef, useCallback, useEffect, useMemo, useRef, useState } from 'react' | |||
| import { useCallback, useEffect, useMemo, useState } from 'react' | |||
| 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 { fetchCurrentWorkspace, fetchLangGeniusVersion, fetchUserProfile } from '@/service/common' | |||
| 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,13 +18,21 @@ export type AppContextValue = { | |||
| isCurrentWorkspaceEditor: boolean | |||
| isCurrentWorkspaceDatasetOperator: boolean | |||
| mutateCurrentWorkspace: VoidFunction | |||
| pageContainerRef: React.RefObject<HTMLDivElement> | |||
| langeniusVersionInfo: LangGeniusVersionResponse | |||
| langGeniusVersionInfo: LangGeniusVersionResponse | |||
| useSelector: typeof useSelector | |||
| isLoadingCurrentWorkspace: boolean | |||
| } | |||
| const initialLangeniusVersionInfo = { | |||
| const userProfilePlaceholder = { | |||
| id: '', | |||
| name: '', | |||
| email: '', | |||
| avatar: '', | |||
| avatar_url: '', | |||
| is_password_set: false, | |||
| } | |||
| const initialLangGeniusVersionInfo = { | |||
| current_env: '', | |||
| current_version: '', | |||
| latest_version: '', | |||
| @@ -50,16 +53,7 @@ const initialWorkspaceInfo: ICurrentWorkspace = { | |||
| } | |||
| const AppContext = createContext<AppContextValue>({ | |||
| apps: [], | |||
| mutateApps: noop, | |||
| userProfile: { | |||
| id: '', | |||
| name: '', | |||
| email: '', | |||
| avatar: '', | |||
| avatar_url: '', | |||
| is_password_set: false, | |||
| }, | |||
| userProfile: userProfilePlaceholder, | |||
| currentWorkspace: initialWorkspaceInfo, | |||
| isCurrentWorkspaceManager: false, | |||
| isCurrentWorkspaceOwner: false, | |||
| @@ -67,8 +61,7 @@ const AppContext = createContext<AppContextValue>({ | |||
| isCurrentWorkspaceDatasetOperator: false, | |||
| mutateUserProfile: noop, | |||
| mutateCurrentWorkspace: noop, | |||
| pageContainerRef: createRef(), | |||
| langeniusVersionInfo: initialLangeniusVersionInfo, | |||
| langGeniusVersionInfo: initialLangGeniusVersionInfo, | |||
| useSelector, | |||
| isLoadingCurrentWorkspace: false, | |||
| }) | |||
| @@ -82,14 +75,11 @@ export type AppContextProviderProps = { | |||
| } | |||
| export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => { | |||
| const pageContainerRef = useRef<HTMLDivElement>(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) | |||
| const [userProfile, setUserProfile] = useState<UserProfileResponse>() | |||
| const [langeniusVersionInfo, setLangeniusVersionInfo] = useState<LangGeniusVersionResponse>(initialLangeniusVersionInfo) | |||
| const [userProfile, setUserProfile] = useState<UserProfileResponse>(userProfilePlaceholder) | |||
| const [langGeniusVersionInfo, setLangGeniusVersionInfo] = useState<LangGeniusVersionResponse>(initialLangGeniusVersionInfo) | |||
| const [currentWorkspace, setCurrentWorkspace] = useState<ICurrentWorkspace>(initialWorkspaceInfo) | |||
| const isCurrentWorkspaceManager = useMemo(() => ['owner', 'admin'].includes(currentWorkspace.role), [currentWorkspace.role]) | |||
| const isCurrentWorkspaceOwner = useMemo(() => currentWorkspace.role === 'owner', [currentWorkspace.role]) | |||
| @@ -101,8 +91,8 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => | |||
| setUserProfile(result) | |||
| const current_version = userProfileResponse.headers.get('x-version') | |||
| const current_env = process.env.NODE_ENV === 'development' ? 'DEVELOPMENT' : userProfileResponse.headers.get('x-env') | |||
| const versionData = await fetchLanggeniusVersion({ url: '/version', params: { current_version } }) | |||
| setLangeniusVersionInfo({ ...versionData, current_version, latest_version: versionData.version, current_env }) | |||
| const versionData = await fetchLangGeniusVersion({ url: '/version', params: { current_version } }) | |||
| setLangGeniusVersionInfo({ ...versionData, current_version, latest_version: versionData.version, current_env }) | |||
| } | |||
| }, [userProfileResponse]) | |||
| @@ -115,17 +105,11 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => | |||
| setCurrentWorkspace(currentWorkspaceResponse) | |||
| }, [currentWorkspaceResponse]) | |||
| if (!appList || !userProfile) | |||
| return <Loading type='app' /> | |||
| return ( | |||
| <AppContext.Provider value={{ | |||
| apps: appList.data, | |||
| mutateApps, | |||
| userProfile, | |||
| mutateUserProfile, | |||
| pageContainerRef, | |||
| langeniusVersionInfo, | |||
| langGeniusVersionInfo, | |||
| useSelector, | |||
| currentWorkspace, | |||
| isCurrentWorkspaceManager, | |||
| @@ -137,7 +121,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => | |||
| }}> | |||
| <div className='flex h-full flex-col overflow-y-auto'> | |||
| {globalThis.document?.body?.getAttribute('data-public-maintenance-notice') && <MaintenanceNotice />} | |||
| <div ref={pageContainerRef} className='relative flex grow flex-col overflow-y-auto overflow-x-hidden bg-background-body'> | |||
| <div className='relative flex grow flex-col overflow-y-auto overflow-x-hidden bg-background-body'> | |||
| {children} | |||
| </div> | |||
| </div> | |||
| @@ -14,7 +14,7 @@ const client = new QueryClient({ | |||
| }, | |||
| }) | |||
| export const TanstackQueryIniter: FC<PropsWithChildren> = (props) => { | |||
| export const TanstackQueryInitializer: FC<PropsWithChildren> = (props) => { | |||
| const { children } = props | |||
| return <QueryClientProvider client={client}> | |||
| {children} | |||
| @@ -88,7 +88,7 @@ export const logout: Fetcher<CommonResponse, { url: string; params: Record<strin | |||
| return get<CommonResponse>(url, params) | |||
| } | |||
| export const fetchLanggeniusVersion: Fetcher<LangGeniusVersionResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => { | |||
| export const fetchLangGeniusVersion: Fetcher<LangGeniusVersionResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => { | |||
| return get<LangGeniusVersionResponse>(url, { params }) | |||
| } | |||