chore: replace all cn comments of fe to en version by volc api (#320)

This commit is contained in:
tecvan
2025-07-31 10:32:15 +08:00
committed by GitHub
parent 716ec0cba8
commit 71f6245a01
2960 changed files with 15545 additions and 15545 deletions

View File

@@ -18,7 +18,7 @@ import { vi, describe, it, expect, beforeEach } from 'vitest';
import { passport } from '@coze-studio/api-schema';
import { passportApi } from '../index';
// 模拟 passport API
// Simulated passport API
vi.mock('@coze-studio/api-schema/passport', () => ({}));
vi.mock('@coze-studio/api-schema', () => ({
passport: {

View File

@@ -42,7 +42,7 @@ export const passportApi = {
updatePassword: async (params: { password: string; email: string }) => {
await passport.PassportWebEmailPasswordResetGet({ ...params, code: '' });
// 更新密码后,当前登录态失效,重置 store
// After updating the password, the current login state is invalid, reset the store
resetUserStore();
},

View File

@@ -40,5 +40,5 @@ export const checkLoginImpl = async () => {
export const checkLogin = () => checkLoginBase(checkLoginImpl);
// 开源版本不支持渠道授权,暂无实现
// The open-source version does not support channel authorization and has not been implemented yet.
export const connector2Redirect: Connector2Redirect = () => undefined;

View File

@@ -43,13 +43,13 @@ describe('useSyncLocalStorageUid', () => {
initialProps: {},
});
// 初始状态:未登录
// Initial status: not logged in
(useLoginStatus as Mock).mockReturnValue('not_login');
(useUserInfo as Mock).mockReturnValue(null);
rerender();
expect(localStorageService.setUserId).toHaveBeenCalledWith();
// 切换到登录状态
// Switch to login status
(useLoginStatus as Mock).mockReturnValue('logined');
(useUserInfo as Mock).mockReturnValue(mockUserInfo);
rerender();

View File

@@ -28,11 +28,11 @@ import { type UserInfo } from '../types';
import { useUserStore } from '../store/user';
/**
* 用于页面初始化时,检查登录状态,并监听登录态失效的接口报错
* 在登录态失效时,会重定向到登录页
* @param needLogin 是否需要登录
* @param checkLogin 检查登录状态的具体实现
* @param goLogin 重定向到登录页的具体实现
* It is used to check the login status when the page is initialized, and listen for the interface error if the login status is invalid.
* When the login status fails, it will be redirected to the login page
* @param needLogin is required
* @Param checkLogin Check the specific implementation of login status
* @Param goLogin Redirect to login page concrete implementation
*/
export const useCheckLoginBase = (
needLogin: boolean,
@@ -54,7 +54,7 @@ export const useCheckLoginBase = (
useEffect(() => {
const isLogined = !!useUserStore.getState().userInfo?.user_id_str;
// 当前页面要求登录,登录检查结果为未登录时,重定向回登录页
// The current page requires login. If the login check result is not logged in, redirect back to the login page.
if (needLogin && isSettled && !isLogined) {
memoizedGoLogin();
}
@@ -71,7 +71,7 @@ export const useCheckLoginBase = (
}
}
};
// ajax 请求后端接口出现未 授权/登录 时,触发该函数
// This function is triggered when the Ajax request backend interface appears not authorized/logged in
handleAPIErrorEvent(APIErrorEvent.UNAUTHORIZED, handleUnauthorized);
return () => {
removeAPIErrorEvent(APIErrorEvent.UNAUTHORIZED, handleUnauthorized);

View File

@@ -22,8 +22,8 @@ import { type LoginStatus } from '../types';
import { useUserStore } from '../store/user';
/**
* @description 用于获取用户登录状态
* @returns 登录状态
* @Description is used to obtain user login status
* @returns login status
*/
export const useLoginStatus = (): LoginStatus =>
useUserStore(state => {
@@ -34,21 +34,21 @@ export const useLoginStatus = (): LoginStatus =>
});
/**
* @description 用于获取用户信息
* @returns 用户信息
* @Description is used to obtain user information
* @returns user information
*/
export const useUserInfo = () => useUserStore(state => state.userInfo);
/**
* @description 当前是否为错误状态
* @returns 是否为错误状态
* @Description Whether it is currently in an error state
* @Returns whether it is an error
*/
export const useHasError = () => useUserStore(state => state.hasError);
const currentUidLSKey = 'coze_current_uid';
/**
* 用于打开多页签情况下,探测其它页签下发生的登出事件并在当前触发提示
* @param alert 触发提示的具体实现
* It is used to detect logout events that occur under other tabs when multiple tabs are opened and trigger a prompt at the current time
* @Param alert trigger prompt specific implementation
*/
export const useAlterOnLogout = (alert: () => void) => {
const visibility = useDocumentVisibility();
@@ -62,7 +62,7 @@ export const useAlterOnLogout = (alert: () => void) => {
useEffect(() => {
if (visibility === 'hidden' && isLogined) {
const lastUserId = useUserStore.getState().userInfo?.user_id_str;
// 登录态下,每次页面从后台回到前台,重新检查一次登录用户是否发生了变化
// In the login state, each time the page returns to the foreground from the background, re-check whether the logged in user has changed.
return () => {
const latestUserId = localStorage.getItem(currentUidLSKey);
if (lastUserId !== latestUserId) {
@@ -72,7 +72,7 @@ export const useAlterOnLogout = (alert: () => void) => {
}
}, [visibility, isLogined]);
// 在登录态变化后,更新本地缓存状态
// Update local cache status after login status changes
useEffect(() => {
if (loginStatus !== 'settling') {
localStorage.setItem(

View File

@@ -15,12 +15,12 @@
*/
/**
* 当前登录账号的用户信息
* User information of the currently logged in account
*/
export interface UserInfo {
app_id: number;
/**
* @deprecated 会因为溢出丢失精度,使用 user_id_str
* @Deprecated will lose precision due to overflow, use user_id_str
*/
user_id: number;
user_id_str: string;
@@ -96,7 +96,7 @@ export interface UserInfo {
name?: string;
[key: string]: unknown;
}; // Record<string, unknown>;
// int值。1审核中2审核通过3审核不通过
// int value. 1 During the review, 2 passed the review, and 3 failed the review.
audit_status: 1 | 2 | 3;
details: Record<string, unknown>;
is_auditing: boolean;
@@ -106,9 +106,9 @@ export interface UserInfo {
}
/**
* 登录状态
* - settling: 登录状态检测中,一般用于首屏,会有一定的延迟
* - not_login: 未登录
* - logined: 已登录
* login status
* - settling: In the login status detection, it is generally used for the first screen, and there will be a certain delay.
* - not_login: not logged in
* - logined: logged in
*/
export type LoginStatus = 'settling' | 'not_login' | 'logined';

View File

@@ -18,25 +18,25 @@ export type OAuth2StateType = 'login' | 'delete_account' | 'oauth';
export interface OAuth2RedirectConfig {
/**
* 最终的OAuth2鉴权信息将作为路由参数跳转这个参数指定目标路由地址注意在目标路由上使用
* useAuthLoginDataRouteFromOAuth2来提取路由参数,并转换成用户中台三方登陆服务(authLogin)的参数;
* 默认值为当前路径名称即不传navigatePath参数时当前路由一定要注册useAuthLoginDataRouteFromOAuth2才有效
* The final OAuth2 authentication information will be redirected as a route parameter, which specifies the target route address. Be careful to use it on the target route
* useAuthLoginDataRouteFromOAuth2 to extract the routing parameters and convert them into the parameters of the user's mid-platform three-party login service (authLogin);
* The default value is the current path name, that is, when the navigatePath parameter is not passed, the current route must be registered useAuthLoginDataRouteFromOAuth2 to be valid
*/
navigatePath?: string;
/**
* OAuth2回调后拿到的鉴权信息的使用场景用于在一些路由组件中区分不符合对应场景的不能用于消费
* The usage scenario of the authentication information obtained after the OAuth2 callback is used to distinguish among some routing components. Those that do not meet the corresponding scenario cannot be used for consumption
*/
type: OAuth2StateType;
/**
* 传递给OAuth2服务器的state字段会在回调时传回用于恢复网页状态
* The state field passed to the OAuth2 server is returned during the callback to restore the state of the webpage
*/
extra?: {
// @ts-expect-error -- linter-disable-autofix
origin?: string;
[x: string]: string; // 用于安全监测
[x: string]: string; // For safety monitoring
// @ts-expect-error -- linter-disable-autofix
encrypt_state?: string; //加密statebind_type 为 4时使用
encrypt_state?: string; //Encrypted state, used when bind_type 4
};
scope?: string;
optionalScope?: string;

View File

@@ -20,8 +20,8 @@ import { type UserInfo } from '../types';
import { useUserStore } from '../store/user';
/**
* 主动触发刷新用户信息
* @param checkLogin 登录检查函数
* Actively trigger to refresh user information
* @param checkLogin check function
*/
export const refreshUserInfoBase = async (
checkLogin: () => Promise<UserInfo>,

View File

@@ -21,14 +21,14 @@ import { type UserInfo, type LoginStatus } from '../types';
import { useUserStore } from '../store/user';
/**
* 获取用户信息
* @returns UserInfo 用户信息
* Acquire user information
* @returns UserInfo
*/
export const getUserInfo = () => useUserStore.getState().userInfo;
/**
* 获取登录状态
* @returns LoginStatus 登录状态
* Get login status
* @returns LoginStatus
*/
export const getLoginStatus = (): LoginStatus => {
const state = useUserStore.getState();

View File

@@ -43,7 +43,7 @@ const Mask: FC<PropsWithChildren> = ({ children }) => (
</div>
);
// 在需要时渲染错误状态 & loading
// Rendering error states when needed & loading
const LoginCheckMask: FC<{ needLogin: boolean; loginOptional: boolean }> = ({
needLogin,
loginOptional,

View File

@@ -44,7 +44,7 @@ import { UserInfoField, type UserInfoFieldProps } from './user-info-field';
import styles from './index.module.less';
// 用户输入 username 自动检查的时间
// The time when the user enters the username to automatically check
export const CHECK_USER_NAME_DEBOUNCE_TIME = 1000;
const WrappedInputWithCount: React.FC<
@@ -213,7 +213,7 @@ export const UserInfoPanel = () => {
});
localStorage.setItem('i18next', newLang === 'en-US' ? 'en' : newLang);
updateProfileEvent.success();
// 更新语言设置需要刷新页面才能生效
// Updating the language settings requires a page refresh to take effect
setTimeout(() => {
window.location.reload();
}, 500);
@@ -310,7 +310,7 @@ export const UserInfoPanel = () => {
setAvatar(userInfo?.avatar_url ?? '');
}, [userInfo]);
// 在进入和离开时均刷新一次用户信息
// Refresh user information once upon entry and exit
useEffect(() => {
refreshUserInfo();
return () => {

View File

@@ -23,7 +23,7 @@ import s from './index.module.less';
export const USER_NAME_MAX_LEN = 20;
interface InputWithCountProps extends InputProps {
// 设置字数限制并显示字数统计
// Set word limits and display word count
getValueLength?: (value?: InputProps['value'] | string) => number;
}

View File

@@ -40,7 +40,7 @@ export const useLogout = (): UseLogoutReturnType => {
onOk={async () => {
await logout();
setVisible(false);
// 跳转到根路径
// Jump to root path
navigate('/');
}}
onCancel={() => {

View File

@@ -122,7 +122,7 @@
}
}
/* 当浏览器窗口的高度大于等于760px, 高度固定 */
/* When the height of the browser window is greater than or equal to 760px, the height is fixed */
@media screen and (min-height: 760px) {
:global {
.semi-modal {
@@ -135,7 +135,7 @@
}
}
/* 当浏览器窗口的高度小于760px时 高度 100vh - 160px */
/* When the height of the browser window is less than 760px, the height is 100vh - 160px. */
@media screen and (max-height: 759px) {
:global {
.semi-modal {

View File

@@ -28,7 +28,7 @@ export interface TabItem {
id: string;
tabName: string;
/**
* @param close 关闭setting弹窗
* @param close settings pop-up
* @returns ReactElement
*/
content: (close?: () => void) => ReactElement;

View File

@@ -1,11 +1,11 @@
.flex-helper {
/* stylelint-disable-next-line value-no-vendor-prefix */
display: -webkit-flex; /* 新版本语法: Chrome 21+ */
display: flex; /* 新版本语法: Opera 12.1, Firefox 22+ */
display: -webkit-box; /* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
display: -moz-box; /* 老版本语法: Firefox (buggy) */
display: -webkit-flex; /* New version syntax: Chrome 21 + */
display: flex; /* New version syntax: Opera 12.1, Firefox 22 + */
display: -webkit-box; /* Old syntax: Safari, iOS, Android browser, older WebKit browsers. */
display: -moz-box; /* Old version syntax: Firefox (buggy) */
/* stylelint-disable-next-line value-no-vendor-prefix */
display: -ms-flexbox; /* 混合版本语法: IE 10 */
display: -ms-flexbox; /* Mixed version syntax: IE 10 */
}
.flex-1-helper {
@@ -35,7 +35,7 @@
.flex-items-center {
/* stylelint-disable-next-line property-no-vendor-prefix */
-webkit-align-items: center; /* Chrome 21+, Safari 6.1+, Opera 15+ */
align-items: center; /* 新语法 */
align-items: center; /* new grammar */
-ms-flex-align: center; /* IE 10 */
}
@@ -43,10 +43,10 @@
.flex-justify-center {
/* stylelint-disable-next-line property-no-vendor-prefix */
-webkit-justify-content: center; /* Chrome 21+, Safari 6.1+ */
justify-content: center; /* 新版浏览器 */
justify-content: center; /* New browser */
-webkit-box-pack: center; /* iOS 6-, Safari 3.1-6 */
-moz-box-pack: center; /* 早期版本的 Firefox */
-moz-box-pack: center; /* Early versions of Firefox */
-ms-flex-pack: center; /* IE 10 */
}

View File

@@ -15,20 +15,20 @@
*/
export function compareVersion(version1: string, version2: string): number {
// 将版本号字符串分割成数字数组,这里使用map(Number)确保转换为数字类型
// Split the version number string into an array of numbers, here use map (Number) to ensure conversion to numeric type
const parts1 = version1.split('.').map(Number);
const parts2 = version2.split('.').map(Number);
// 计算出最长的版本号长度
// Calculate the longest version length
const maxLength = Math.max(parts1.length, parts2.length);
// 逐个比较版本号中的每个部分
// Compare each part of the version number one by one
for (let i = 0; i < maxLength; i++) {
// 如果某个版本号在这个位置没有对应的数字则视为0
// If a version number does not have a corresponding number at this position, it is treated as 0.
const part1 = i < parts1.length ? parts1[i] : 0;
const part2 = i < parts2.length ? parts2[i] : 0;
// 比较两个版本号的当前部分
// Compare the current parts of two version numbers
if (part1 > part2) {
return 1;
}
@@ -37,6 +37,6 @@ export function compareVersion(version1: string, version2: string): number {
}
}
// 如果所有部分都相等,则版本号相等
// If all parts are equal, then the version numbers are equal
return 0;
}

View File

@@ -68,7 +68,7 @@ const INTERNATIONAL_BROWSER_DOWNLOAD_CONFIG: DownloadConfig = {
};
/**
* 目前看起来 移动端 / PC 版本一致无需区分,后期如果区分,在这里通过条件区分
* At present, it seems that the mobile end/PC version is the same without distinction. If it is distinguished later, it will be distinguished by conditions here.
*/
export const testLowVersionBrowse = () => testPCVersion();
@@ -81,7 +81,7 @@ const testPCVersion = () => {
const { name, version } = browserInfo;
// 显示的判断,用 includes 类型推断不正确
// The displayed judgment, incorrectly inferred with the includes type
if (name === 'bot' || name === 'react-native' || name === 'node') {
return null;
}

View File

@@ -16,7 +16,7 @@
export const isMobileFromUA = () => {
const { userAgent } = navigator;
// 检查是否为移动设备
// Check if it is a mobile device
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
userAgent,
);

View File

@@ -15,7 +15,7 @@
*/
/**
* @file 开源版暂时不提供企业管理功能,本文件中导出的方法用于未来拓展使用。
* The @file open-source version does not provide enterprise management functions for the time being. The methods exported in this file are for future expansion.
*/
import { useCallback } from 'react';

View File

@@ -15,7 +15,7 @@
*/
/**
* @file 开源版暂时不提供企业管理功能,本文件中导出的方法用于未来拓展使用。
* The @file open-source version does not provide enterprise management functions for the time being. The methods exported in this file are for future expansion.
*/
import { type GetEnterpriseResponseData } from '@coze-arch/bot-api/pat_permission_api';
@@ -25,44 +25,44 @@ export interface CurrentEnterpriseInfoProps extends GetEnterpriseResponseData {
organization_id: string | undefined;
}
/**
* 获取当前企业信息。
* 如果当前企业为个人版则返回null。
* 否则,返回当前企业信息,包括企业信息和组织ID
* Acquire current corporate information.
* If the current enterprise is a personal edition, null is returned.
* Otherwise, return current enterprise information, including enterprise information and organization ID.
* @example
* const { organization_id, enterprise_id } = useCurrentEnterpriseInfo();
* @returns {(GetEnterpriseResponseData & { organization_id: string | undefined }) | null} 当前企业信息或null
* @Returns { (GetEnterpriseResponseData & {organization_id: string | undefined}) | null} current enterprise information or null
*/
export const useCurrentEnterpriseInfo: () => CurrentEnterpriseInfoProps | null =
() => null;
/**
* 获取当前企业ID
* 如果当前企业类型为个人版,则返回约定的字符串。
* 否则返回当前企业的ID。
* @returns {string} 当前企业ID
* Obtain the current enterprise ID.
* If the current enterprise type is Personal Edition, the agreed string is returned.
* Otherwise, return the ID of the current enterprise.
* @Returns {string} current enterprise ID
*/
export const useCurrentEnterpriseId = () =>
useEnterpriseStore(store => store.enterpriseId);
/**
* 检查当前企业是否为个人版。
* @returns {boolean} 如果当前企业为个人版则返回true否则返回false。
* Check whether the current enterprise is a personal version.
* @Returns {boolean} True if the current enterprise is a personal edition, false otherwise.
*/
export const useIsCurrentPersonalEnterprise = () => true;
/**
* 获取当前企业的角色列表。
* 如果当前企业类型为个人版,则返回空数组。
* 否则,返回当前企业的角色类型列表,如果列表不存在,则返回空数组。
* @returns {Array} 当前企业的角色列表
* Get a list of roles for the current enterprise.
* If the current enterprise type is Personal, an empty array is returned.
* Otherwise, a list of the current enterprise's role types is returned, or an empty array if the list does not exist.
* @Returns {Array} list of roles for the current enterprise
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const useCurrentEnterpriseRoles = (): any[] => [];
/** 是否是企业版 */
/** Is it the enterprise version? */
export const useIsEnterpriseLevel = () => false;
/** 是否是团队版 */
/** Is it the team version? */
export const useIsTeamLevel = () => false;
export const useIsCurrentEnterpriseInit = () =>

View File

@@ -15,14 +15,14 @@
*/
/**
* @file 开源版暂时不提供企业管理功能,本文件中导出的方法用于未来拓展使用。
* The @file open-source version does not provide enterprise management functions for the time being. The methods exported in this file are for future expansion.
*/
import { useEnterpriseStore } from '../stores/enterprise';
/**
* 获取企业列表的hook。
* 从企业store中获取企业列表并返回企业信息列表。
* @returns {Array} 企业信息列表
* Hook to get the business list.
* Get the business list from the business store and return the list of business information.
* @Returns {Array} Enterprise Information List
*/
export const useEnterpriseList = () => {
const list = useEnterpriseStore(store => store.enterpriseList);

View File

@@ -15,7 +15,7 @@
*/
/**
* @file 开源版暂时不提供企业管理功能,本文件中导出的方法用于未来拓展使用。
* The @file open-source version does not provide enterprise management functions for the time being. The methods exported in this file are for future expansion.
*/
export { PERSONAL_ENTERPRISE_ID } from './constants';
@@ -34,6 +34,6 @@ export {
CurrentEnterpriseInfoProps,
} from './hooks/use-current-enterprise-info';
// 工具方法
// tool method
export { switchEnterprise } from './utils/switch-enterprise';
export { isPersonalEnterprise } from './utils/personal';

View File

@@ -15,7 +15,7 @@
*/
/**
* @file 开源版暂时不提供企业管理功能,本文件中导出的方法用于未来拓展使用。
* The @file open-source version does not provide enterprise management functions for the time being. The methods exported in this file are for future expansion.
*/
/* eslint-disable @typescript-eslint/no-empty-function */
import { devtools } from 'zustand/middleware';
@@ -74,7 +74,7 @@ export const useEnterpriseStore = create<
setIsEnterpriseListInit: (_: boolean) => {},
setEnterpriseList: (_: ListEnterpriseResponseData) => {},
setIsEnterpriseExist: (_: boolean) => {},
// 获取企业信息,可连续调用,不存在异步竞争问题。
// Obtaining enterprise information can be continuously invoked without asynchronous competition.
fetchEnterprise: (_: string) => {},
}),
{

View File

@@ -15,11 +15,11 @@
*/
/**
* @file 开源版暂时不提供企业管理功能,本文件中导出的方法用于未来拓展使用。
* The @file open-source version does not provide enterprise management functions for the time being. The methods exported in this file are for future expansion.
*/
import { PERSONAL_ENTERPRISE_ID } from '../constants';
// 检查企业是否为个人版
// Check if the business is a personal version
export const isPersonalEnterprise = (enterpriseId?: string) =>
enterpriseId === PERSONAL_ENTERPRISE_ID;

View File

@@ -15,11 +15,11 @@
*/
/**
* @file 开源版暂时不提供企业管理功能,本文件中导出的方法用于未来拓展使用。
* The @file open-source version does not provide enterprise management functions for the time being. The methods exported in this file are for future expansion.
*/
/**
* 切换企业
* @param {string} enterpriseId - 企业ID
* Switch Enterprise
* @param {string} enterpriseId - Enterprise ID
*/
export const switchEnterprise = (_: string) => Promise.resolve();

View File

@@ -41,21 +41,21 @@ import {
subscribeUserAuthInfos as subscribeUserAuthInfosImpl,
} from '@coze-foundation/account-adapter';
/** @deprecated 使用 getLoginStatus */
/** @deprecated using getLoginStatus */
export const getIsSettled = (() =>
getLoginStatus() !== 'settling') satisfies typeof getIsSettledOfSdk;
/** @deprecated 使用 getLoginStatus */
/** @deprecated using getLoginStatus */
export const getIsLogined = (() =>
getLoginStatus() === 'logined') satisfies typeof getIsLoginedOfSdk;
export const getUserInfo = getUserInfoImpl satisfies typeof getUserInfoOfSdk;
export const getUserAuthInfos =
getUserAuthInfosImpl satisfies typeof getUserAuthInfosOfSdk;
/** @deprecated 使用 useLoginStatus */
/** @deprecated useLoginStatus */
export const useIsSettled = (() => {
const status = useLoginStatus();
return status !== 'settling';
}) satisfies typeof useIsSettledOfSdk;
/** @deprecated 使用 useLoginStatus */
/** @deprecated useLoginStatus */
export const useIsLogined = (() => {
const status = useLoginStatus();
return status === 'logined';

View File

@@ -26,14 +26,14 @@ export const useHasSider = () => {
const queryParams = new URLSearchParams(location.search);
const pageMode = queryParams.get('page_mode');
// 优先使用 page_mode 参数判断是否为全屏模式
// Priority is given to using page_mode parameters to determine whether it is full screen mode
if (config.pageModeByQuery && pageMode === 'modal') {
return false;
}
const notCheckLoginPage =
(config.requireAuth && config.requireAuthOptional) || !config.requireAuth;
// 未登录时也可访问的页面
// Pages that can be accessed without logging in
if (config.hasSider && notCheckLoginPage && !isLogined) {
return false;
}

View File

@@ -38,7 +38,7 @@ export const GlobalLayout: FC = () => {
const update = useUpdate();
const currentLocale = userInfo?.locale ?? navigator.language ?? 'en-US';
// 历史原因en-US 需要被转换为 en
// For historical reasons, en-US needs to be converted to en.
const transformedCurrentLocale =
currentLocale === 'en-US' ? 'en' : currentLocale;
@@ -46,7 +46,7 @@ export const GlobalLayout: FC = () => {
if (userInfo && I18n.language !== transformedCurrentLocale) {
localStorage.setItem('i18next', transformedCurrentLocale);
I18n.setLang(transformedCurrentLocale);
// 强制更新,否则切换语言不生效
// Force an update, otherwise the language switch will not take effect
update();
}
}, [userInfo, transformedCurrentLocale, update]);

View File

@@ -31,8 +31,8 @@ import { useResetStoreOnLogout } from './use-reset-store-on-logout';
import { useInitCommonConfig } from './use-init-common-config';
/**
* 所有初始化的逻辑收敛到这里
* 注意登录态需要自行处理
* All initialization logic converges here
* Note that the login status needs to be handled by yourself.
*/
export const useAppInit = () => {
const { requireAuth, requireAuthOptional, loginFallbackPath } =

View File

@@ -15,7 +15,7 @@
*/
/**
* @file 开源版暂不支持后台配置,用于未来拓展
* @File open source version does not support background configuration for future expansion
*/
import { useEffect } from 'react';

View File

@@ -29,7 +29,7 @@ export const useCreateBotAction = ({
urlSearch?: string;
currentSpaceId?: string;
}) => {
// 创建 bot 功能
// Create bot function
const newWindowRef = useRef<Window | null>(null);
const openWindow = () => {
newWindowRef.current = window.open();

View File

@@ -16,20 +16,20 @@
export function removeGlobalLoading() {
const spin = document.querySelector('#global-spin-wrapper');
// 对于不支持MutationObserver的浏览器直接隐藏 loading避免影响正常页面的展示
// Hide loading directly for browsers that do not support MutationObserver to avoid affecting the display of normal pages
if (!window.MutationObserver && spin) {
(spin as HTMLElement).style.display = 'none';
return;
}
const targetNode = document.querySelector('#root');
const observerOptions = {
childList: true, // 观察目标子节点的变化,是否有添加或者删除
attributes: true, // 观察属性变动
subtree: true, // 观察后代节点,默认为 false
childList: true, // Observe the changes of the target sub-node and see if any are added or deleted
attributes: true, // Observe attribute changes
subtree: true, // Observe the descendant nodes, the default is false
};
const observer = new MutationObserver(function callback(mutationList) {
// root 节点有任何变化就取消 loading并取消观测
// Cancel loading if there is any change in the root node and cancel the observation
mutationList.forEach(mutation => {
if (spin) {
(spin as HTMLElement).style.display = 'none';

View File

@@ -1,4 +1,4 @@
/* stylelint-disable declaration-no-important -- 历史代码为避免引入新BUG暂不修复 */
/* Stylelint-disable declaration-no-important -- historical code, not fixed to avoid introducing new bugs */
.wrapper {
width: 100%;
height: 100%;

View File

@@ -30,7 +30,7 @@ import { useSpaceStore, useSpaceApp } from '@coze-foundation/space-store';
import s from './index.module.less';
// i18n 的配置,对齐 starling 文案后再替换
// The configuration of i18n, align the starling copy and then replace it.
export const GlobalError: FC = () => {
const navigate = useNavigate();
const spaceApp = useSpaceApp();
@@ -87,7 +87,7 @@ export const GlobalError: FC = () => {
const spaceId =
id ??
getPersonalSpaceID() ??
// 企业下无个人空间,缺省跳转到第一个空间
// There is no personal space under the enterprise, so jump to the first space by default.
useSpaceStore.getState().spaceList[0]?.id;
url = spaceId ? `/space/${spaceId}/${spaceApp}` : '/space';
} else if (base && base in BaseEnum) {

View File

@@ -65,7 +65,7 @@ export const GlobalLayoutActionBtn: FC<LayoutButtonItem> = ({
data-testid={dataTestId}
/>
);
// 如果 tooltip 为空,则不显示 tooltip
// If tooltip is empty, tooltip is not displayed
return (
<>
{tooltip ? (

View File

@@ -41,7 +41,7 @@ export const GLobalLayoutMenuItem: FC<LayoutMenuItem> = ({
let isActive = false;
let newPath = '';
// 如果 path 是数组,则取第一个匹配的路径
// If path is an array, take the first matching path
if (Array.isArray(path)) {
isActive = path.some(p => location.pathname.startsWith(p));
newPath = path.find(p => location.pathname.startsWith(p)) || path[0];

View File

@@ -53,7 +53,7 @@ export const GlobalLayoutSider: FC<Omit<LayoutProps, 'hasSider'>> = ({
return (
<div className="pl-8px py-8px h-full">
<div className={siderStyle}>
{/* 主导航 */}
{/* main navigation */}
<div
className={classNames(
mainMenuStyle,
@@ -83,7 +83,7 @@ export const GlobalLayoutSider: FC<Omit<LayoutProps, 'hasSider'>> = ({
{footer}
</Space>
</div>
{/* 二级导航 */}
{/* secondary navigation */}
<SubMenu />
</div>
</div>

View File

@@ -38,7 +38,7 @@ export const useLayoutResponsive = () => {
useEffect(() => {
if (config.showMobileTips) {
if (!mobileTips && isMobile()) {
openMobileTipsModal(); // 不适配移动端弹窗提示
openMobileTipsModal(); // Not suitable for mobile end pop-up window prompt
setMobileTips(true);
}

View File

@@ -19,7 +19,7 @@ import { IconSideFoldOutlined } from '@coze-arch/bot-icons';
import { useOpenGlobalLayoutSideSheet } from './global-layout/hooks';
// 用于在移动端模式开启侧边栏
// Use to open sidebar in mobile end mode
export const SideSheetMenu = () => {
const open = useOpenGlobalLayoutSideSheet();

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
/** 布局框架 */
/** layout framework */
export { SideSheetMenu } from './components/side-sheet-menu';
export { GlobalError } from './components/global-error';
export { BackButton } from './components/back-button';

View File

@@ -18,7 +18,7 @@ import { devtools } from 'zustand/middleware';
import { create } from 'zustand';
interface SignMobileStore {
/** 标识有没有弹出过提示 */
/** Has the logo ever popped up? */
mobileTips: boolean;
}

View File

@@ -22,9 +22,9 @@ const LOCAL_STORAGE_KEY = '__coz_biz_cache__';
describe('LocalStorageService', () => {
beforeEach(() => {
// 清除 localStorage
// Clear localStorage
localStorage.clear();
// 重置 userId
// Reset userId
localStorageService.setUserId(undefined);
});
@@ -49,7 +49,7 @@ describe('LocalStorageService', () => {
localStorageService.setValue(permanentKey, value);
// 等待 throttle
// Waiting for throttle
await new Promise(resolve => setTimeout(resolve, 400));
const storedData = JSON.parse(
@@ -87,15 +87,15 @@ describe('LocalStorageService', () => {
const value2 = 'user2-value';
const userId2 = 'test-user-id-2';
// 第一个用户的数据
// The first user's data
localStorageService.setValue(userBindKey, value1);
// 切换到第二个用户
// Switch to the second user
localStorageService.setUserId(userId2);
localStorageService.setValue(userBindKey, value2);
expect(localStorageService.getValue(userBindKey)).toBe(value2);
// 切回第一个用户
// Switch back to the first user
localStorageService.setUserId(userId);
expect(localStorageService.getValue(userBindKey)).toBe(value1);
});
@@ -111,7 +111,7 @@ describe('LocalStorageService', () => {
localStorageService.on('change', changeHandler);
localStorageService.setValue(permanentKey, value);
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
expect(changeHandler).toHaveBeenCalled();
@@ -147,15 +147,15 @@ describe('LocalStorageService', () => {
const userId = 'test-user-id';
const value = 'test-value';
// 先设置值
// Set the value first
localStorageService.setUserId(userId);
localStorageService.setValue('coachmark', value);
localStorageService.setUserId(undefined);
// 异步获取值
// Get value asynchronously
const valuePromise = localStorageService.getValueSync('coachmark');
// 设置 userId
// Set userId
setTimeout(() => {
localStorageService.setUserId(userId);
}, 0);

View File

@@ -32,7 +32,7 @@ describe('useValue', () => {
const value = 'test-value';
localStorageService.setValue(permanentKey, value);
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
const { result } = renderHook(() => useValue(permanentKey));
@@ -42,12 +42,12 @@ describe('useValue', () => {
it('当值改变时应该更新', async () => {
const { result } = renderHook(() => useValue(permanentKey));
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
await act(async () => {
localStorageService.setValue(permanentKey, 'new-value');
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
});
@@ -57,14 +57,14 @@ describe('useValue', () => {
it('当值被删除时应该返回 undefined', async () => {
localStorageService.setValue(permanentKey, 'test-value');
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
const { result } = renderHook(() => useValue(permanentKey));
await act(async () => {
localStorageService.setValue(permanentKey, undefined);
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
});
@@ -74,18 +74,18 @@ describe('useValue', () => {
it('卸载时应该清理事件监听', async () => {
const { unmount } = renderHook(() => useValue(permanentKey));
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
unmount();
// 确保不会触发已卸载组件的状态更新
// Make sure that status updates for uninstalled components are not triggered
localStorageService.setValue(permanentKey, 'new-value');
// 如果事件监听没有被清理,这里会抛出 React 警告
// If the event listener is not cleaned up, a React warning will be thrown here
});
describe('用户相关的值', () => {
// 使用一个确定绑定了用户的 key
// Use a key that confirms the user is bound.
const userBindKey = 'coachmark' as const;
const userId = 'test-user-id';
@@ -96,7 +96,7 @@ describe('useValue', () => {
it('应该返回当前用户的值', async () => {
localStorageService.setValue(userBindKey, 'user-value');
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
const { result } = renderHook(() => useValue(userBindKey));
@@ -107,7 +107,7 @@ describe('useValue', () => {
const userId2 = 'test-user-id-2';
localStorageService.setValue(userBindKey, 'user1-value');
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
const { result } = renderHook(() => useValue(userBindKey));
@@ -116,7 +116,7 @@ describe('useValue', () => {
await act(async () => {
localStorageService.setUserId(userId2);
localStorageService.setValue(userBindKey, 'user2-value');
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
});
@@ -124,7 +124,7 @@ describe('useValue', () => {
await act(async () => {
localStorageService.setUserId(userId);
// 等待事件触发
// Wait for the event to fire
await new Promise(resolve => setTimeout(resolve, 0));
});

View File

@@ -72,7 +72,7 @@ describe('解析工具函数', () => {
it('应该返回空对象当永久缓存数据格式无效', () => {
const data = {
permanent: {
key: 123, // 应该是字符串
key: 123, // Should be string.
},
};
expect(paseLocalStorageValue(JSON.stringify(data))).toEqual({});
@@ -82,7 +82,7 @@ describe('解析工具函数', () => {
const data = {
userRelated: {
'user-1': {
key: 123, // 应该是字符串
key: 123, // Should be string.
},
},
};

View File

@@ -16,7 +16,7 @@
import { type LocalStorageCacheConfig } from './types';
// 统一维护 key 定义避免出现冲突
// Maintain key definitions uniformly to avoid conflicts
export const LOCAL_STORAGE_CACHE_KEYS = [
'coachmark',
'workspace-spaceId',

View File

@@ -37,8 +37,8 @@ class LocalStorageService extends EventEmitter {
}, throttleWait);
document.addEventListener('visibilitychange', () => {
/**
* 页签进入后台后,通过操作其它页签,可能导致 #state 状态不是最新的
* 所以页签重新激活后需要同步一次 localStorage 的数据
* After the tab enters the background, by operating other tabs, the #state status may not be the latest
* So after the tab is reactivated, the data of localStorage needs to be synchronized once.
*/
if (document.visibilityState === 'visible') {
this.#initState();

View File

@@ -27,7 +27,7 @@ const isValidDataItem = (data: unknown): data is CacheDataItems => {
const isObject = (value: unknown): value is object =>
!!value && typeof value === 'object' && value !== null;
// 判断本地缓存中的值是否与 LocalStorageCacheData 类型定义匹配
// Determines if a value in the local cache matches the LocalStorageCacheData type definition
const isValidCacheData = (value: unknown): value is LocalStorageCacheData => {
if (!isObject(value)) {
return false;

View File

@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// eslint-disable-next-line @coze-arch/no-batch-import-or-export, @typescript-eslint/consistent-type-imports
import * as zustand from 'zustand';
import { act } from '@testing-library/react';
const { create: actualCreate, createStore: actualCreateStore } =
// @ts-expect-error -- UT 忽略
// @ts-expect-error -- UT ignored
await vi.importActual<typeof zustand>('zustand');
// a variable to hold reset functions for all stores declared in the app

View File

@@ -27,7 +27,7 @@ vi.mock('@coze-arch/bot-error', () => ({
CustomError: vi.fn(),
}));
// FIXME 改为按需 mock
// FIXME changed to mock on demand
vi.mock('@coze-arch/bot-api', () => ({
DeveloperApi: {
GetUserAuthList: vi
@@ -50,7 +50,7 @@ vi.mock('@coze-arch/bot-api', () => ({
// .mockResolvedValueOnce({ code: 1 })
// .mockResolvedValueOnce({ code: 0 })
// .mockResolvedValueOnce({ code: 0 })
// mock 缺失 personal store && 轮询失败
// Mock missing personal store & & poll failed
.mockResolvedValueOnce({ code: 0 })
.mockResolvedValueOnce({ code: 0 })
.mockResolvedValueOnce({ code: 0 })

View File

@@ -15,7 +15,7 @@
*/
export enum ReportEventNames {
EmptySpaceList = 'empty_space_List', // 空间列表为空
PollingSpaceList = 'polling_space_list', // 轮训空间列表
EmptySpaceList = 'empty_space_List', // Space list is empty
PollingSpaceList = 'polling_space_list', // Rotation space list
}

View File

@@ -39,9 +39,9 @@ interface SpaceStoreState {
recentlyUsedSpaceList: BotSpace[];
loading: false | Promise<SpaceInfo | undefined>;
inited?: boolean;
createdTeamSpaceNum: number; // 个人创建的团队空间计数
createdTeamSpaceNum: number; // Count of team spaces created by individuals
maxTeamSpaceNum: number;
/** @deprecated 使用 spaceList & maxTeamSpaceNum */
/** @deprecated spaceList & maxTeamSpaceNum */
spaces: {
bot_space_list: BotSpace[];
has_personal_space: boolean;
@@ -56,7 +56,7 @@ interface SpaceStoreAction {
getSpaceId: () => string;
getPersonalSpaceID: () => string | undefined;
checkSpaceID: (spaceID: string) => boolean;
/** @deprecated 通过 id 索引 */
/** @deprecated by id index */
setSpace: (spaceId?: string, isBotDetailIframe?: boolean) => void | never;
createSpace: (
request: SaveSpaceV2Request,

View File

@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// eslint-disable-next-line @coze-arch/no-batch-import-or-export, @typescript-eslint/consistent-type-imports
import * as zustand from 'zustand';
import { act } from '@testing-library/react';
const { create: actualCreate, createStore: actualCreateStore } =
// @ts-expect-error -- UT 忽略
// @ts-expect-error -- UT ignored
await vi.importActual<typeof zustand>('zustand');
// a variable to hold reset functions for all stores declared in the app

View File

@@ -17,12 +17,12 @@
import { useLocation } from 'react-router-dom';
/**
* 从URL上获取工作空间子模块
* Get the workspace submodule from the URL
* @param pathname
* @returns 工作子模块字符串,如果不匹配则返回 undefined
* @Returns the working submodule string, or undefined if it doesn't match
*/
const getSpaceApp = (pathname: string): string | undefined => {
// 以 /space/ 开头,后面跟 spaceId再跟子模块只允许字母、数字、-、_
// Start with /space/, followed by spaceId, followed by submodules (only letters, numbers, -, _ allowed)
const match = pathname.match(/^\/space\/[^/]+\/([A-Za-z0-9_-]+)/);
return match ? match[1] : undefined;
};

View File

@@ -23,7 +23,7 @@ import { type BotSpace } from '@coze-arch/bot-api/developer_api';
export const useRefreshSpaces = (refresh?: boolean) => {
const [loading, setLoading] = useState(true);
const enterpriseInfo = useCurrentEnterpriseInfo();
// 企业发生变化,重新获取空间列表
// Businesses change, regain the list of spaces
useEffect(() => {
if (refresh || !useSpaceStore.getState().inited) {
setLoading(true);

View File

@@ -20,13 +20,13 @@ import { useDestorySpace } from '@coze-common/auth';
import { useInitSpaceRole } from '@coze-common/auth-adapter';
const SpaceIdContainer = ({ spaceId }: { spaceId: string }) => {
// 空间组件销毁时清空对应space数据
// When the space component is destroyed, empty the corresponding space data
useDestorySpace(spaceId);
// 初始化空间权限数据
// Initialize spatial permission data
const isCompleted = useInitSpaceRole(spaceId);
// isCompleted 的 判断条件很重要确保了在Space空间内能够获取到空间的权限数据。
// isCompleted, the judgment condition is very important to ensure that the permission data of the space can be obtained in the Space space.
return isCompleted ? <Outlet /> : null;
};

View File

@@ -45,7 +45,7 @@ const getSubPath = (type: IntelligenceType | undefined) => {
return 'project-ide';
}
if (type === IntelligenceType.Bot) {
//跳转至 Bot编辑页后续会改成新的URL/space/:spaceId/agent/:agentId
//Jump to the Bot edit page, which will be changed to a new URL/space/: spaceId/agent/: agentId later.
return 'bot';
}
return '';
@@ -63,7 +63,7 @@ export const FavoritesListItem: FC<IntelligenceData> = ({
basic_info = {},
type,
}) => {
// 取消收藏
// Cancel Favorite
const clickToUnfavorite = async () => {
try {
const res: FavoriteProductResponse =
@@ -76,7 +76,7 @@ export const FavoritesListItem: FC<IntelligenceData> = ({
entity_id: id,
});
if (res.code === 0) {
// 取消收藏成功,刷新收藏列表
// Cancel the collection successfully, refresh the collection list
cozeMitt.emit('refreshFavList', {
id,
numDelta: -1,
@@ -113,7 +113,7 @@ export const FavoritesListItem: FC<IntelligenceData> = ({
need_login: true,
have_access: true,
});
//跳转至 Bot编辑页后续会改成新的URL/space/:spaceId/agent/:agentId
//Jump to the Bot edit page, which will be changed to a new URL/space/: spaceId/agent/: agentId later.
window.open(getIntelligenceNavigateUrl({ basic_info, type }), '_blank');
}}
data-testid="workspace.favorites.list.item"

View File

@@ -101,7 +101,7 @@ const getFavoritesList = async ({
};
/**
* ahooks 的 useInfiniteScroll 返回的对象引用会变,本方法返回一个引用不变的对象,仅此而已,不用关注其声明和实现
* The object reference returned by the useInfiniteScroll of ahooks will change. This method returns an object with unchanged references, nothing more, regardless of its declaration and implementation
*/
const useInfiniteScrollRef: typeof useInfiniteScroll = <
T extends { list: unknown[] },
@@ -123,8 +123,8 @@ export const FavoritesList: FC = () => {
const containerRef = useRef<HTMLDivElement>(null);
// 用一个引用不变的 req便于 effect 中的 handler 拿到最新的 loading 状态
// (将 loading 放进 effect 的 deps 中并不能解决问题,因为上一次的闭包中 getFavoritesList 前后的 loading 状态已经固定不会变了,导致上一次执行出错)
// Use an invariant req to make it easier for the handler in the effect to get the latest loading status
// (Putting loading into the deps of effect doesn't solve the problem, because the loading state before and after getFavoritesList in the last closure has been fixed and will not change, resulting in an error in the last execution)
const req = useInfiniteScrollRef<FEIntelligenceListData>(
async dataSource =>
await getFavoritesList({
@@ -143,7 +143,7 @@ export const FavoritesList: FC = () => {
useEffect(() => {
const handler = async (refreshFavListParams: RefreshFavListParams) => {
if (req.loading || req.loadingMore) {
// 处理竞态问题,优先保证列表滚动加载,下同
// Deal with the race problem, give priority to ensuring the rolling loading of the list, the same as below
return;
}
@@ -151,14 +151,14 @@ export const FavoritesList: FC = () => {
const mutateData = await getFavoritesList({
spaceId,
spaceType,
// Q:为什么要专门设置 pageSize
// AuseInfiniteScroll 有个 bug/featuremutate 后不会立即触发高度检测
// 这要从它的 loadmore 触发逻辑讲起,正常是监听 scroll 动作,检测高度,从而判断是否需要 loadmore
// 但假如首次请求回来的数据就不足一屏高度,没有 overflow 无法触发 scroll 动作,怎么办?
// 因此 useInfiniteScroll 会在 run、reload 之类的行为完成后立即做一次高度检测来判断是否要继续 loadmore
// 但是!它却唯独不会在 mutate 后做高度检测,导致 mutate 出来的数据不足一屏,就再也无法 loadmore
// 因此这里手动计算一下需要 mutate 的数据量。
// 如果后续 pageSize 太大有问题,那还可以继续改造一下 useInfiniteScrollRef使 mutate 动作实际执行 reload但拦截其 loading 属性返回 false
// Q: Why set the pageSize specifically?
// A: useInfiniteScroll has a bug/feature that does not trigger height detection immediately after mutating
// This starts with its loadmore trigger logic. Normally, it monitors the scroll action and detects the height to determine whether loadmore is required.
// But what if the data requested for the first time is less than one screen height, and the scroll action cannot be triggered without overflow?
// Therefore, useInfiniteScroll will perform a height check immediately after running, reloading, etc. to determine whether to continue loadmore.
// However! It will not do height detection after mutating, resulting in less than one screen of mutated data, and it can no longer loadmore
// So here we manually calculate the amount of data that needs to be mutated.
// If there is a problem with the subsequent pageSize being too large, you can continue to modify the useInfiniteScrollRef so that the mutate action actually executes reload, but intercepts its loading property and returns false.
pageSize: Math.max(
currLength
? currLength + refreshFavListParams.numDelta
@@ -169,7 +169,7 @@ export const FavoritesList: FC = () => {
if (req.loading || req.loadingMore) {
return;
}
// 使用 mutate 静默加载,直接更新视图,不展示 loading 效果
// Use mutate silent loading to update the view directly without displaying the loading effect
req.mutate(mutateData);
};
cozeMitt.on('refreshFavList', handler);
@@ -177,7 +177,7 @@ export const FavoritesList: FC = () => {
}, [spaceId, spaceType]);
return (
// 这里有一个很坑,滚动蒙层如果直接挂在滚动画布元素上会一起滚动,所以这里需要单独包一层(往上方一层)
// There is a very pit here. If the scrolling overlay is directly hung on the scrolling canvas element, it will scroll together, so it needs to be wrapped in a separate layer (one layer above).
<div className={classNames('w-full h-full flex flex-col')}>
<>
<Space

View File

@@ -1,4 +1,4 @@
/* 定义滚动条默认隐藏样式 styled-scrollbar-hidden */
/* Define the scrollbar default hidden style styled-scrollbar-hidden */
.styled-scrollbar-hidden {
scrollbar-width: none; /* Firefox */
@@ -44,7 +44,7 @@
.list-bottom-mask::after,
.list-top-mask::before {
pointer-events: none; /* 确保伪元素不阻挡用户交互 */
pointer-events: none; /* Make sure that pseudo-elements do not block user interaction */
content: '';
position: absolute;

View File

@@ -77,38 +77,38 @@ export const useInitSpace = ({
return;
}
// 如果未指定spaceId则跳转到兜底的space下的项目开发子路由
// If spaceId is not specified, jump to the project development subroute under the space of the backseat
if (!spaceId) {
// 拉取空间列表
// Pull space list
await useSpaceStore.getState().fetchSpaces(true);
// 获取个人空间Id
// Get Personal Space Id
const personalSpaceID = useSpaceStore.getState().getPersonalSpaceID();
// 空间列表的第一个空间
// The first space in the space list
const firstSpaceID = useSpaceStore.getState().spaceList[0]?.id;
// 未指定spaceId的兜底空间
// SpaceId not specified
const fallbackSpaceID = personalSpaceID ?? firstSpaceID ?? '';
// 检查指定的spaceId是否可以访问
// Checks if the specified spaceId is accessible
const { checkSpaceID } = useSpaceStore.getState();
// 无工作空间,提示创建
// No workspace, prompt to create
if (!fallbackSpaceID) {
Toast.warning(I18n.t('enterprise_workspace_default_tips2_toast'));
} else {
// 获取兜底的跳转URL
// Get the jump URL of the back cover.
const targetURL = await getFallbackWorkspaceURL(
fallbackSpaceID,
'develop',
checkSpaceID,
);
// 跳转
// jump
navigate(targetURL);
}
} else {
// 拉取空间列表
// Pull space list
await fetchSpacesWithSpaceId?.(spaceId);
if (!useSpaceStore.getState().checkSpaceID(spaceId)) {
// 当 space id 在space 列表找不到时,抛出错误
// Throws an error when the space id cannot be found in the space list
capture(
new CustomError(ReportEventNames.errorPath, 'space id error', {
customGlobalErrorConfig: {
@@ -119,7 +119,7 @@ export const useInitSpace = ({
}),
);
} else {
// 更新space storespaceId
// Update space store spaceId
useSpaceStore.getState().setSpace(spaceId);
}
}