未验证 提交 767c6d30 编写于 作者: P Peter Pan 提交者: GitHub

feat: remember selected runs between pages (close #774) (#788)

上级 211b2b59
import {GlobalDispatchContext, GlobalStateContext, globalState} from '~/hooks/useGlobalState';
import React, {useReducer} from 'react';
import type {FunctionComponent} from 'react';
import type {GlobalState as GlobalStateType} from '~/hooks/useGlobalState';
interface GlobalDispatch {
(state: GlobalStateType, newState: Partial<GlobalStateType>): GlobalStateType;
}
const GlobalState: FunctionComponent = ({children}) => {
const [state, dispatch] = useReducer<GlobalDispatch>((state, newState) => ({...state, ...newState}), globalState);
return (
<GlobalStateContext.Provider value={state}>
<GlobalDispatchContext.Provider value={dispatch}>{children}</GlobalDispatchContext.Provider>
</GlobalStateContext.Provider>
);
};
export default GlobalState;
import {createContext, useContext} from 'react';
import type {Dispatch} from 'react';
export interface GlobalState {
runs: string[];
}
export const globalState: GlobalState = {
runs: []
};
export const GlobalStateContext = createContext<GlobalState>(globalState);
export const GlobalDispatchContext = createContext<Dispatch<Partial<GlobalState>>>(() => void 0);
const useGlobalState = () => [useContext(GlobalStateContext), useContext(GlobalDispatchContext)] as const;
export default useGlobalState;
...@@ -6,6 +6,7 @@ import groupBy from 'lodash/groupBy'; ...@@ -6,6 +6,7 @@ import groupBy from 'lodash/groupBy';
import intersectionBy from 'lodash/intersectionBy'; import intersectionBy from 'lodash/intersectionBy';
import queryString from 'query-string'; import queryString from 'query-string';
import uniq from 'lodash/uniq'; import uniq from 'lodash/uniq';
import useGlobalState from '~/hooks/useGlobalState';
import {useLocation} from 'react-router-dom'; import {useLocation} from 'react-router-dom';
import {useRunningRequest} from '~/hooks/useRequest'; import {useRunningRequest} from '~/hooks/useRequest';
...@@ -13,6 +14,7 @@ type Tags = Record<string, string[]>; ...@@ -13,6 +14,7 @@ type Tags = Record<string, string[]>;
type State = { type State = {
initRuns: string[]; initRuns: string[];
globalRuns: string[];
runs: Run[]; runs: Run[];
selectedRuns: Run[]; selectedRuns: Run[];
initTags: Tags; initTags: Tags;
...@@ -86,12 +88,16 @@ const reducer = (state: State, action: Action): State => { ...@@ -86,12 +88,16 @@ const reducer = (state: State, action: Action): State => {
switch (action.type) { switch (action.type) {
case ActionType.initRuns: case ActionType.initRuns:
const initRuns = action.payload; const initRuns = action.payload;
const initRunsGlobalRuns = state.globalRuns.length ? state.globalRuns : initRuns;
const initRunsRuns = attachRunColor(initRuns); const initRunsRuns = attachRunColor(initRuns);
const initRunsSelectedRuns = state.selectedRuns.filter(run => initRuns.includes(run.label)); const initRunsSelectedRuns = state.globalRuns.length
? initRunsRuns.filter(run => initRunsGlobalRuns.includes(run.label))
: initRunsRuns;
const initRunsTags = groupTags(initRunsSelectedRuns, state.initTags); const initRunsTags = groupTags(initRunsSelectedRuns, state.initTags);
return { return {
...state, ...state,
initRuns, initRuns,
globalRuns: initRunsGlobalRuns,
runs: initRunsRuns, runs: initRunsRuns,
selectedRuns: initRunsSelectedRuns, selectedRuns: initRunsSelectedRuns,
tags: initRunsTags, tags: initRunsTags,
...@@ -111,6 +117,7 @@ const reducer = (state: State, action: Action): State => { ...@@ -111,6 +117,7 @@ const reducer = (state: State, action: Action): State => {
const setSelectedRunsTags = groupTags(action.payload, state.initTags); const setSelectedRunsTags = groupTags(action.payload, state.initTags);
return { return {
...state, ...state,
globalRuns: action.payload.map(run => run.label),
selectedRuns: action.payload, selectedRuns: action.payload,
tags: setSelectedRunsTags, tags: setSelectedRunsTags,
selectedTags: setSelectedRunsTags selectedTags: setSelectedRunsTags
...@@ -146,6 +153,8 @@ const useTagFilter = (type: string, running: boolean) => { ...@@ -146,6 +153,8 @@ const useTagFilter = (type: string, running: boolean) => {
const {data, loading, error} = useRunningRequest<TagsData>(`/${type}/tags`, running); const {data, loading, error} = useRunningRequest<TagsData>(`/${type}/tags`, running);
const [globalState, globalDispatch] = useGlobalState();
const runs: string[] = useMemo(() => data?.runs ?? [], [data]); const runs: string[] = useMemo(() => data?.runs ?? [], [data]);
const tags: Tags = useMemo( const tags: Tags = useMemo(
() => () =>
...@@ -164,6 +173,7 @@ const useTagFilter = (type: string, running: boolean) => { ...@@ -164,6 +173,7 @@ const useTagFilter = (type: string, running: boolean) => {
const [state, dispatch] = useReducer(reducer, { const [state, dispatch] = useReducer(reducer, {
initRuns: [], initRuns: [],
globalRuns: globalState.runs,
runs: [], runs: [],
selectedRuns: [], selectedRuns: [],
initTags: {}, initTags: {},
...@@ -176,18 +186,23 @@ const useTagFilter = (type: string, running: boolean) => { ...@@ -176,18 +186,23 @@ const useTagFilter = (type: string, running: boolean) => {
[query] [query]
); );
const runsFromQuery = useMemo(
() => (queryRuns.length ? state.runs.filter(run => queryRuns.includes(run.label)) : state.runs),
[state.runs, queryRuns]
);
const onChangeRuns = useCallback((runs: Run[]) => dispatch({type: ActionType.setSelectedRuns, payload: runs}), []); const onChangeRuns = useCallback((runs: Run[]) => dispatch({type: ActionType.setSelectedRuns, payload: runs}), []);
const onChangeTags = useCallback((tags: Tag[]) => dispatch({type: ActionType.setSelectedTags, payload: tags}), []); const onChangeTags = useCallback((tags: Tag[]) => dispatch({type: ActionType.setSelectedTags, payload: tags}), []);
useEffect(() => dispatch({type: ActionType.initRuns, payload: runs || []}), [runs]); useEffect(() => dispatch({type: ActionType.initRuns, payload: runs || []}), [runs]);
useEffect(() => dispatch({type: ActionType.setSelectedRuns, payload: runsFromQuery}), [runsFromQuery]);
useEffect(() => dispatch({type: ActionType.initTags, payload: tags || {}}), [tags]); useEffect(() => dispatch({type: ActionType.initTags, payload: tags || {}}), [tags]);
useEffect(() => {
if (queryRuns.length) {
const runs = state.runs.filter(run => queryRuns.includes(run.label));
dispatch({
type: ActionType.setSelectedRuns,
payload: runs.length ? runs : state.runs
});
}
}, [queryRuns, state.runs]);
useEffect(() => globalDispatch({runs: state.globalRuns}), [state.globalRuns, globalDispatch]);
const tagsWithSingleRun = useMemo( const tagsWithSingleRun = useMemo(
() => () =>
state.tags.reduce<TagWithSingleRun[]>((prev, {runs, ...item}) => { state.tags.reduce<TagWithSingleRun[]>((prev, {runs, ...item}) => {
......
...@@ -2,6 +2,7 @@ import '~/utils/i18n'; ...@@ -2,6 +2,7 @@ import '~/utils/i18n';
import App from './App'; import App from './App';
import BodyLoading from '~/components/BodyLoading'; import BodyLoading from '~/components/BodyLoading';
import GlobalState from '~/components/GlobalState';
import {GlobalStyle} from '~/utils/style'; import {GlobalStyle} from '~/utils/style';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
...@@ -22,7 +23,9 @@ ReactDOM.render( ...@@ -22,7 +23,9 @@ ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<GlobalStyle /> <GlobalStyle />
<React.Suspense fallback={<BodyLoading />}> <React.Suspense fallback={<BodyLoading />}>
<App /> <GlobalState>
<App />
</GlobalState>
</React.Suspense> </React.Suspense>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById('root')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册