|
|
|
@ -1,70 +1,201 @@
|
|
|
|
|
// 导入 React 库,用于构建组件
|
|
|
|
|
import * as React from 'react';
|
|
|
|
|
// 导入 react-redux 库中的 connect 函数,用于连接 React 组件和 Redux 存储
|
|
|
|
|
import { connect as realConnect } from 'react-redux';
|
|
|
|
|
// 导入 redux 库中的 combineReducers、createStore 和 Dispatch 函数,用于管理 Redux 状态
|
|
|
|
|
import { combineReducers, createStore, Dispatch } from 'redux';
|
|
|
|
|
// 从 './data' 模块中导入所有内容,可能包括 reducer、action 等
|
|
|
|
|
import * as data from './data';
|
|
|
|
|
// 从 './view' 模块中导入所有内容,可能包括 reducer、action 等
|
|
|
|
|
import * as view from './view';
|
|
|
|
|
// 从 './vscode' 模块中导入 API 对象,用于与 VSCode 环境交互
|
|
|
|
|
import { API } from './vscode';
|
|
|
|
|
|
|
|
|
|
// 使用 redux 的 combineReducers 函数将多个 reducer 合并为一个
|
|
|
|
|
const reducers = combineReducers({
|
|
|
|
|
// 引入 data 模块的 reducer,用于处理数据相关的状态
|
|
|
|
|
data: data.reducer,
|
|
|
|
|
// 引入 view 模块的 reducer,用于处理视图相关的状态
|
|
|
|
|
view: view.reducer,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 定义应用程序的状态
|
|
|
|
|
* @interface State
|
|
|
|
|
* @property {data.IState} data - 数据相关的状态
|
|
|
|
|
* @property {view.IState} view - 视图相关的状态
|
|
|
|
|
*/
|
|
|
|
|
export interface State {
|
|
|
|
|
// 数据相关的状态
|
|
|
|
|
data: data.IState;
|
|
|
|
|
// 视图相关的状态
|
|
|
|
|
view: view.IState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 定义一个联合类型,包含所有可能的 action
|
|
|
|
|
* @type Action
|
|
|
|
|
* @property {data.actions.Action} data - 数据相关的 action
|
|
|
|
|
* @property {view.actions.Action} view - 视图相关的 action
|
|
|
|
|
*/
|
|
|
|
|
export type Action = data.actions.Action | view.actions.Action;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 定义应用程序的默认状态
|
|
|
|
|
* @const DEFAULT_STATE
|
|
|
|
|
* @type {State}
|
|
|
|
|
* @property {data.DEFAULT_STATE} data - 数据相关的默认状态
|
|
|
|
|
* @property {view.DEFAULT_STATE} view - 视图相关的默认状态
|
|
|
|
|
*/
|
|
|
|
|
export const DEFAULT_STATE: State = {
|
|
|
|
|
// 数据相关的默认状态
|
|
|
|
|
data: data.DEFAULT_STATE,
|
|
|
|
|
// 视图相关的默认状态
|
|
|
|
|
view: view.DEFAULT_STATE,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建一个 Redux store,用于存储应用程序的状态
|
|
|
|
|
* @const STORE
|
|
|
|
|
* @type {Store<State, Action>}
|
|
|
|
|
* @param {Reducer<State, Action>} reducers - 应用程序的 reducer,用于处理状态的变化
|
|
|
|
|
* @param {State} preloadedState - 初始状态,如果存在则从 API 中获取,否则使用默认状态
|
|
|
|
|
* @param {Enhancer} enhancer - 可选的 store enhancer,用于增强 store 的功能
|
|
|
|
|
*/
|
|
|
|
|
export const STORE = createStore(reducers, API.getState() || DEFAULT_STATE, undefined);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化 data 模块的 store
|
|
|
|
|
* @function initStore
|
|
|
|
|
* @param {Store<State, Action>} store - 要初始化的 store
|
|
|
|
|
*/
|
|
|
|
|
data.initStore(STORE);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化 view 模块的 store
|
|
|
|
|
* @function initStore
|
|
|
|
|
* @param {Store<State, Action>} store - 要初始化的 store
|
|
|
|
|
*/
|
|
|
|
|
view.initStore(STORE);
|
|
|
|
|
|
|
|
|
|
// 保存原始的 dispatch 方法,以便在重写的 dispatch 方法中调用
|
|
|
|
|
const oldDispatch = STORE.dispatch.bind(STORE);
|
|
|
|
|
|
|
|
|
|
// 重写 STORE 的 dispatch 方法
|
|
|
|
|
STORE.dispatch = (action) => {
|
|
|
|
|
// 打印日志,显示当前正在分发的 action
|
|
|
|
|
console.log('STORE.dispatch', action);
|
|
|
|
|
// 调用原始的 dispatch 方法,分发 action
|
|
|
|
|
oldDispatch(action);
|
|
|
|
|
// 返回 action,以便在调用 dispatch 方法后可以继续处理 action
|
|
|
|
|
return action;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 订阅 STORE 的变化,当 STORE 发生变化时,调用 API.setState 方法将当前状态同步到 VSCode 的 API 中
|
|
|
|
|
STORE.subscribe(() => API.setState(STORE.getState()));
|
|
|
|
|
|
|
|
|
|
// 当导航事件发生时,向 VSCode 发送一个消息,消息类型为 'navigated',内容包含当前的视图状态
|
|
|
|
|
API.postMessage({ type: 'navigated', view: STORE.getState().view.view });
|
|
|
|
|
|
|
|
|
|
// Makes debugging easier (and this is inside our WebView context anyway)
|
|
|
|
|
// 将 STORE 挂载到全局 window 对象上,以便在其他地方访问
|
|
|
|
|
(window as any).STORE = STORE;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取组件的属性类型
|
|
|
|
|
* @typeparam C - 组件类型,可以是类组件或函数组件
|
|
|
|
|
* @returns 组件的属性类型
|
|
|
|
|
*/
|
|
|
|
|
type GetComponentProps<C> = C extends React.ComponentClass<infer P, any> ? P : (C extends React.FunctionComponent<infer P2> ? P2 : {});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取组件的状态类型
|
|
|
|
|
* @typeparam C - 组件类型,可以是类组件或函数组件
|
|
|
|
|
* @returns 组件的状态类型,如果组件没有状态,则返回空对象
|
|
|
|
|
*/
|
|
|
|
|
type GetComponentState<C> = C extends React.ComponentClass<any, infer S> ? S : {};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 从类型 T 中排除指定的键 K
|
|
|
|
|
* @typeparam T - 要排除键的类型
|
|
|
|
|
* @typeparam K - 要排除的键
|
|
|
|
|
* @returns 一个新的类型,包含 T 中除了 K 之外的所有键
|
|
|
|
|
*/
|
|
|
|
|
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取组件的自有属性
|
|
|
|
|
* @typeparam C - 组件类型,可以是类组件或函数组件
|
|
|
|
|
* @typeparam P - 要排除的属性
|
|
|
|
|
* @returns 组件的自有属性类型,即排除了 P 中的属性
|
|
|
|
|
*/
|
|
|
|
|
type OwnProps<C extends (React.ComponentClass<any> | React.FunctionComponent<any>), P> = Omit<GetComponentProps<C>, keyof P>;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ConnectReturn 接口定义了 connect 函数的返回类型
|
|
|
|
|
* @interface ConnectReturn
|
|
|
|
|
* @typeparam C - 组件类型,可以是类组件或函数组件
|
|
|
|
|
*/
|
|
|
|
|
interface ConnectReturn<C extends (React.ComponentClass<any> | React.FunctionComponent<any>)> {
|
|
|
|
|
/**
|
|
|
|
|
* 第一个重载签名:接受一个函数 stateToProps,返回一个新的组件类
|
|
|
|
|
* @template TStateProps, TState
|
|
|
|
|
* @param { (state: TState, ownProps: OwnProps<C, TStateProps>) => TStateProps } stateToProps - 用于从 Redux 状态中提取 props 的函数
|
|
|
|
|
* @returns { React.ComponentClass<Omit<GetComponentProps<C>, keyof TStateProps>, GetComponentState<C>> } - 返回一个新的组件类,该组件类的 props 是从 stateToProps 函数中提取的,并且排除了 TStateProps 中的键
|
|
|
|
|
*/
|
|
|
|
|
<TStateProps, TState = State>(
|
|
|
|
|
stateToProps: (state: TState, ownProps: OwnProps<C, TStateProps>) => TStateProps
|
|
|
|
|
): React.ComponentClass<Omit<GetComponentProps<C>, keyof TStateProps>, GetComponentState<C>>;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 第二个重载签名:接受两个函数 stateToProps 和 dispatchToProps,返回一个新的组件类
|
|
|
|
|
* @template TStateProps, TDispatchProps, TState
|
|
|
|
|
* @param { (state: TState, ownProps: Omit<GetComponentProps<C>, keyof (TStateProps & TDispatchProps)>) => TStateProps } stateToProps - 用于从 Redux 状态中提取 props 的函数
|
|
|
|
|
* @param { (dispatch: Dispatch<Action>, ownProps: Omit<GetComponentProps<C>, keyof TStateProps & TDispatchProps>) => TDispatchProps } dispatchToProps - 用于从 Redux dispatch 函数中提取 props 的函数
|
|
|
|
|
* @returns { React.ComponentClass<Omit<GetComponentProps<C>, keyof (TStateProps & TDispatchProps)>, GetComponentState<C>> } - 返回一个新的组件类,该组件类的 props 是从 stateToProps 和 dispatchToProps 函数中提取的,并且排除了 TStateProps 和 TDispatchProps 中的键
|
|
|
|
|
*/
|
|
|
|
|
<TStateProps, TDispatchProps, TState = State>(
|
|
|
|
|
stateToProps: (state: TState, ownProps: Omit<GetComponentProps<C>, keyof (TStateProps & TDispatchProps)>) => TStateProps,
|
|
|
|
|
dispatchToProps: (dispatch: Dispatch<Action>, ownProps: Omit<GetComponentProps<C>, keyof TStateProps & TDispatchProps>) => TDispatchProps,
|
|
|
|
|
): React.ComponentClass<Omit<GetComponentProps<C>, keyof (TStateProps & TDispatchProps)>, GetComponentState<C>>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 连接 React 组件到 Redux 存储的高阶组件
|
|
|
|
|
* @function connect
|
|
|
|
|
* @template TComponent - 要连接的组件类型,可以是类组件或函数组件
|
|
|
|
|
* @param {TComponent} component - 要连接的组件
|
|
|
|
|
* @returns {ConnectReturn<TComponent>} - 返回一个新的组件,该组件已经连接到 Redux 存储
|
|
|
|
|
* @remarks
|
|
|
|
|
* 这个函数是一个高阶组件,它接受一个组件作为参数,并返回一个新的组件,这个新的组件已经连接到 Redux 存储。
|
|
|
|
|
* 它可以接受两个参数:stateToProps 和 dispatchToProps。stateToProps 是一个函数,它接受 Redux 存储的状态作为参数,并返回一个对象,这个对象将被用作新组件的 props。
|
|
|
|
|
* dispatchToProps 是一个函数,它接受 Redux 的 dispatch 函数作为参数,并返回一个对象,这个对象将被用作新组件的 props。
|
|
|
|
|
* 如果只提供了 stateToProps 参数,那么 dispatchToProps 将默认使用 Redux 的 dispatch 函数。
|
|
|
|
|
*/
|
|
|
|
|
export function connect<TComponent extends (React.ComponentClass<any> | React.FunctionComponent<any>)>(component: TComponent): ConnectReturn<TComponent> {
|
|
|
|
|
return (stateToProps: any, dispatchToProps?: any) => realConnect(stateToProps, dispatchToProps)(component) as any;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function pickProperties<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K> {
|
|
|
|
|
/**
|
|
|
|
|
* 从对象中提取指定的属性
|
|
|
|
|
* @template T - 对象的类型
|
|
|
|
|
* @template K - 要提取的属性的键的类型,必须是 T 的键的子集
|
|
|
|
|
* @param {T} obj - 要从中提取属性的对象
|
|
|
|
|
* @param {...K} keys - 要提取的属性的键
|
|
|
|
|
* @returns {Pick<T, K>} - 一个新的对象,包含了从原对象中提取的指定属性
|
|
|
|
|
* @remarks
|
|
|
|
|
* 这个函数接受一个对象和一个或多个键作为参数,并返回一个新的对象,该对象只包含传入的键所对应的属性。
|
|
|
|
|
* 如果传入的键不存在于原对象中,则该键对应的属性值在新对象中为 undefined。
|
|
|
|
|
*/
|
|
|
|
|
export function pickProperties<T, K extends keyof T>(obj: T,...keys: K[]): Pick<T, K> {
|
|
|
|
|
// 创建一个新的对象,用于存储从原对象中提取的属性
|
|
|
|
|
const res: Pick<T, K> = {} as any;
|
|
|
|
|
// 遍历传入的键列表
|
|
|
|
|
for (const key of keys) {
|
|
|
|
|
// 将原对象中对应键的值赋给新对象中的对应键
|
|
|
|
|
res[key] = obj[key];
|
|
|
|
|
}
|
|
|
|
|
// 返回新对象,其中包含了从原对象中提取的指定属性
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
}
|