2021-08-10 23:57:13 +00:00
|
|
|
import { Reducer, useCallback, useMemo, useReducer } from 'react';
|
2021-06-15 04:21:06 +00:00
|
|
|
import { useSelector } from 'react-redux';
|
2021-08-10 23:57:13 +00:00
|
|
|
import { useAsync } from 'react-use';
|
2021-07-22 23:04:56 +00:00
|
|
|
|
2021-08-20 15:12:36 +00:00
|
|
|
import { isRemoteProject } from '../../models/project';
|
|
|
|
import { BackendProject } from '../../sync/types';
|
|
|
|
import { BackendProjectWithTeam } from '../../sync/vcs/normalize-backend-project-team';
|
|
|
|
import { pullBackendProject } from '../../sync/vcs/pull-backend-project';
|
2021-07-22 23:04:56 +00:00
|
|
|
import { VCS } from '../../sync/vcs/vcs';
|
2021-06-15 04:21:06 +00:00
|
|
|
import { showAlert } from '../components/modals';
|
2021-10-14 14:59:45 +00:00
|
|
|
import { selectActiveProject, selectAllWorkspaces, selectIsLoggedIn, selectRemoteProjects, selectSettings } from '../redux/selectors';
|
2021-06-23 16:40:48 +00:00
|
|
|
import { useSafeReducerDispatch } from './use-safe-reducer-dispatch';
|
|
|
|
|
|
|
|
interface State {
|
|
|
|
loading: boolean;
|
2021-08-20 15:12:36 +00:00
|
|
|
localBackendProjects: BackendProject[];
|
|
|
|
remoteBackendProjects: BackendProjectWithTeam[];
|
|
|
|
pullingBackendProjects: Record<string, boolean>;
|
2021-06-23 16:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const initialState: State = {
|
|
|
|
loading: false,
|
2021-08-20 15:12:36 +00:00
|
|
|
localBackendProjects: [],
|
|
|
|
remoteBackendProjects: [],
|
|
|
|
pullingBackendProjects: {},
|
2021-06-23 16:40:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
type Action =
|
2021-08-20 15:12:36 +00:00
|
|
|
| { type: 'loadBackendProjects' }
|
2021-09-01 14:50:26 +00:00
|
|
|
| { type: 'saveBackendProjects'; local: State['localBackendProjects']; remote: State['remoteBackendProjects']}
|
|
|
|
| { type: 'startPullingBackendProject'; backendProjectId: string }
|
|
|
|
| { type: 'stopPullingBackendProject'; backendProjectId: string };
|
2021-06-23 16:40:48 +00:00
|
|
|
|
|
|
|
const reducer: Reducer<State, Action> = (prevState, action) => {
|
|
|
|
switch (action.type) {
|
2021-08-20 15:12:36 +00:00
|
|
|
case 'loadBackendProjects':
|
2021-06-23 16:40:48 +00:00
|
|
|
return { ...prevState, loading: true };
|
2021-08-20 15:12:36 +00:00
|
|
|
case 'saveBackendProjects':
|
|
|
|
return { ...prevState, localBackendProjects: action.local, remoteBackendProjects: action.remote, loading: false };
|
|
|
|
case 'startPullingBackendProject':
|
|
|
|
return { ...prevState, pullingBackendProjects: { ...prevState.pullingBackendProjects, [action.backendProjectId]: true } };
|
|
|
|
case 'stopPullingBackendProject':
|
|
|
|
return { ...prevState, pullingBackendProjects: { ...prevState.pullingBackendProjects, [action.backendProjectId]: false } };
|
2021-06-23 16:40:48 +00:00
|
|
|
default:
|
|
|
|
return prevState;
|
|
|
|
}
|
|
|
|
};
|
2021-06-15 04:21:06 +00:00
|
|
|
|
|
|
|
export const useRemoteWorkspaces = (vcs?: VCS) => {
|
|
|
|
// Fetch from redux
|
|
|
|
const workspaces = useSelector(selectAllWorkspaces);
|
2021-08-20 15:12:36 +00:00
|
|
|
const activeProject = useSelector(selectActiveProject);
|
|
|
|
const remoteProjects = useSelector(selectRemoteProjects);
|
2021-08-05 23:30:31 +00:00
|
|
|
const isLoggedIn = useSelector(selectIsLoggedIn);
|
2021-10-14 14:59:45 +00:00
|
|
|
const { incognitoMode } = useSelector(selectSettings);
|
2021-08-05 23:30:31 +00:00
|
|
|
|
2021-06-15 04:21:06 +00:00
|
|
|
// Local state
|
2021-08-20 15:12:36 +00:00
|
|
|
const [{ loading, localBackendProjects, remoteBackendProjects, pullingBackendProjects }, _dispatch] = useReducer(reducer, initialState);
|
2021-06-23 16:40:48 +00:00
|
|
|
const dispatch = useSafeReducerDispatch(_dispatch);
|
2021-06-15 04:21:06 +00:00
|
|
|
|
2021-08-20 15:12:36 +00:00
|
|
|
// Refresh remote backend project
|
2021-06-15 04:21:06 +00:00
|
|
|
const refresh = useCallback(async () => {
|
2021-08-20 15:12:36 +00:00
|
|
|
if (!vcs || !isLoggedIn || !isRemoteProject(activeProject)) {
|
2021-06-15 04:21:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-20 15:12:36 +00:00
|
|
|
dispatch({ type: 'loadBackendProjects' });
|
|
|
|
const remoteBackendProjects = await vcs.remoteBackendProjects(activeProject.remoteId);
|
|
|
|
const localBackendProjects = await vcs.localBackendProjects();
|
|
|
|
dispatch({ type: 'saveBackendProjects', local: localBackendProjects, remote: remoteBackendProjects });
|
|
|
|
}, [vcs, isLoggedIn, activeProject, dispatch]);
|
2021-06-15 04:21:06 +00:00
|
|
|
|
2021-08-20 15:12:36 +00:00
|
|
|
// Find remote backend project that haven't been pulled
|
|
|
|
const missingBackendProjects = useMemo(() => remoteBackendProjects.filter(({ id, rootDocumentId }) => {
|
|
|
|
const localBackendProjectExists = localBackendProjects.find(p => p.id === id);
|
2021-06-15 04:21:06 +00:00
|
|
|
const workspaceExists = workspaces.find(w => w._id === rootDocumentId);
|
|
|
|
// Mark as missing if:
|
2021-08-20 15:12:36 +00:00
|
|
|
// - the backend project doesn't yet exists locally
|
|
|
|
// - the backend project exists locally but somehow the workspace doesn't anymore
|
|
|
|
return !(workspaceExists && localBackendProjectExists);
|
|
|
|
}), [localBackendProjects, remoteBackendProjects, workspaces]);
|
2021-06-15 04:21:06 +00:00
|
|
|
|
2021-08-20 15:12:36 +00:00
|
|
|
// Pull a remote backend project
|
|
|
|
const pull = useCallback(async (backendProject: BackendProjectWithTeam) => {
|
2021-06-15 04:21:06 +00:00
|
|
|
if (!vcs) {
|
|
|
|
throw new Error('VCS is not defined');
|
|
|
|
}
|
|
|
|
|
2021-08-20 15:12:36 +00:00
|
|
|
dispatch({ type: 'startPullingBackendProject', backendProjectId: backendProject.id });
|
2021-06-15 04:21:06 +00:00
|
|
|
|
|
|
|
try {
|
2021-08-20 15:12:36 +00:00
|
|
|
// Clone old VCS so we don't mess anything up while working on other backend projects
|
2021-06-15 04:21:06 +00:00
|
|
|
const newVCS = vcs.newInstance();
|
2021-08-20 15:12:36 +00:00
|
|
|
// Remove all backend projects for workspace first
|
|
|
|
await newVCS.removeBackendProjectsForRoot(backendProject.rootDocumentId);
|
2021-06-15 04:21:06 +00:00
|
|
|
|
2021-08-20 15:12:36 +00:00
|
|
|
await pullBackendProject({ vcs: newVCS, backendProject, remoteProjects });
|
2021-06-15 04:21:06 +00:00
|
|
|
|
|
|
|
await refresh();
|
|
|
|
} catch (err) {
|
|
|
|
showAlert({
|
|
|
|
title: 'Pull Error',
|
|
|
|
message: `Failed to pull workspace. ${err.message}`,
|
|
|
|
});
|
|
|
|
} finally {
|
2021-08-20 15:12:36 +00:00
|
|
|
dispatch({ type: 'stopPullingBackendProject', backendProjectId: backendProject.id });
|
2021-06-15 04:21:06 +00:00
|
|
|
}
|
2021-08-20 15:12:36 +00:00
|
|
|
}, [vcs, refresh, remoteProjects, dispatch]);
|
2021-06-15 04:21:06 +00:00
|
|
|
|
|
|
|
// If the refresh callback changes, refresh
|
2021-10-14 14:59:45 +00:00
|
|
|
useAsync(async () => {
|
|
|
|
if (!incognitoMode) {
|
|
|
|
await refresh();
|
|
|
|
}
|
|
|
|
}, [refresh, incognitoMode]);
|
2021-06-15 04:21:06 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
loading,
|
2021-08-20 15:12:36 +00:00
|
|
|
missingBackendProjects,
|
|
|
|
pullingBackendProjects,
|
2021-06-15 04:21:06 +00:00
|
|
|
refresh,
|
|
|
|
pull,
|
|
|
|
};
|
|
|
|
};
|