Skip to content

Commit

Permalink
Add cluster context switching (#624)
Browse files Browse the repository at this point in the history
  • Loading branch information
gzdunek authored Mar 4, 2022
1 parent 2a03a7a commit 86ad396
Show file tree
Hide file tree
Showing 48 changed files with 936 additions and 549 deletions.
2 changes: 1 addition & 1 deletion web/packages/teleterm/src/mainProcess/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConfigService } from '../services/config';
import { Kind } from 'teleterm/ui/services/docs/types';
import { Kind } from 'teleterm/ui/services/workspacesService';

export type RuntimeSettings = {
dev: boolean;
Expand Down
7 changes: 5 additions & 2 deletions web/packages/teleterm/src/ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import AppContextProvider from './appContextProvider';
import AppContext from './appContext';
import ThemeProvider from './ThemeProvider';
import { LayoutManager } from './LayoutManager';
import { AppInitializer } from 'teleterm/ui/AppInitializer';

const App: React.FC<{ ctx: AppContext }> = ({ ctx }) => {
const { appearance } = ctx.mainProcessClient.configService.get();
Expand All @@ -17,8 +18,10 @@ const App: React.FC<{ ctx: AppContext }> = ({ ctx }) => {
<DndProvider backend={HTML5Backend}>
<AppContextProvider value={ctx}>
<ThemeProvider appearanceConfig={appearance}>
<LayoutManager />
<ModalsHost />
<AppInitializer>
<LayoutManager />
<ModalsHost />
</AppInitializer>
</ThemeProvider>
</AppContextProvider>
</DndProvider>
Expand Down
18 changes: 18 additions & 0 deletions web/packages/teleterm/src/ui/AppInitializer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { ReactNode, useEffect } from 'react';
import { useAppContext } from 'teleterm/ui/appContextProvider';

interface AppInitializerProps {
children?: ReactNode;
}

export function AppInitializer(props: AppInitializerProps) {
const ctx = useAppContext();
useEffect(() => {
const { rootClusterUri } = ctx.statePersistenceService.getWorkspaces();
if (rootClusterUri) {
ctx.workspacesService.setActiveWorkspace(rootClusterUri);
}
}, []);

return <>{props.children}</>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export const Notfound = () => {

function renderState(state: ClustersServiceState) {
const appContext = new MockAppContext();
appContext.docsService.update = () => null;
appContext.workspacesService.getActiveWorkspaceDocumentService().update =
() => null;
appContext.clustersService.state = state;

const doc = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { Text, Flex, Box, ButtonPrimary } from 'design';
import * as types from 'teleterm/ui/services/docs/types';
import * as types from 'teleterm/ui/services/workspacesService';
import Document from 'teleterm/ui/Document';
import { useAppContext } from 'teleterm/ui/appContextProvider';
import ClusterCtx, {
Expand Down
10 changes: 0 additions & 10 deletions web/packages/teleterm/src/ui/DocumentCluster/clusterContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,23 +187,13 @@ const useClusterContext = () => {
return React.useContext(ClusterReactContext);
};

export type ClusterNavItem = {
location: NavLocation;
title: string;
};

export type NavLocation =
| '/resources/'
| '/resources/databases'
| '/resources/servers'
| '/resources/apps'
| '/resources/kubes';

export type ClusterTopNavItem = {
navLocation: NavLocation;
title: string;
};

export default ClusterContext;

export { ClusterContextProvider, ClusterReactContext, useClusterContext };
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import React from 'react';
import { Text, Flex, Box, ButtonPrimary } from 'design';
import Document from 'teleterm/ui/Document';
import * as Alerts from 'design/Alert';
import * as types from 'teleterm/ui/services/docs/types';
import * as types from 'teleterm/ui/services/workspacesService';
import LinearProgress from 'teleterm/ui/components/LinearProgress';
import { GatewayProtocol } from 'teleterm/ui/services/clusters/types';
import useDocumentGateway, { State } from './useDocumentGateway';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ limitations under the License.

import React from 'react';
import { useAppContext } from 'teleterm/ui/appContextProvider';
import * as types from 'teleterm/ui/services/docs/types';
import * as types from 'teleterm/ui/services/workspacesService';
import useAsync from 'teleterm/ui/useAsync';
import { useWorkspaceDocumentsService } from 'teleterm/ui/Documents';

export default function useGateway(doc: types.DocumentGateway) {
const ctx = useAppContext();
const workspaceDocumentsService = useWorkspaceDocumentsService();
const gateway = ctx.clustersService.findGateway(doc.gatewayUri);
const connected = !!gateway;

Expand All @@ -32,7 +34,7 @@ export default function useGateway(doc: types.DocumentGateway) {
user: doc.targetUser,
});

ctx.docsService.update(doc.uri, {
workspaceDocumentsService.update(doc.uri, {
gatewayUri: gw.uri,
});
}
Expand Down Expand Up @@ -69,7 +71,7 @@ export default function useGateway(doc: types.DocumentGateway) {

React.useEffect(() => {
if (disconnectAttempt.status === 'success') {
ctx.docsService.close(doc.uri);
workspaceDocumentsService.close(doc.uri);
}
}, [disconnectAttempt.status]);

Expand Down
6 changes: 4 additions & 2 deletions web/packages/teleterm/src/ui/DocumentHome/DocumentHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ limitations under the License.
import React from 'react';
import { Text, Box, Flex } from 'design';
import Document from 'teleterm/ui/Document';
import * as types from 'teleterm/ui/services/docs/types';
import * as types from 'teleterm/ui/services/workspacesService/documentsService';

//TODO: remove

export default function DocumentHome(props: PropTypes) {
const { visible } = props;
Expand All @@ -42,5 +44,5 @@ const Key = props => <Text p={1} as="span" {...props} bg="primary.light" />;

type PropTypes = {
visible: boolean;
doc: types.DocumentHome;
// doc: types.DocumentHome;
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import React from 'react';
import Document from 'teleterm/ui/Document';
import { Flex, Text, ButtonPrimary } from 'design';
import { useReconnect, State } from './useReconnect';
import * as types from 'teleterm/ui/services/docs/types';
import * as types from 'teleterm/ui/services/workspacesService';
import { Danger } from 'design/Alert';

export default function Container(props: DocumentReconnectProps) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@ limitations under the License.
import { useEffect } from 'react';
import { useAppContext } from 'teleterm/ui/appContextProvider';
import { IAppContext } from 'teleterm/ui/types';
import * as types from 'teleterm/ui/services/docs/types';
import * as types from 'teleterm/ui/services/workspacesService';
import { PtyCommand } from 'teleterm/services/pty/types';
import useAsync from 'teleterm/ui/useAsync';
import { DocumentsService } from 'teleterm/ui/services/workspacesService';
import { useWorkspaceDocumentsService } from 'teleterm/ui/Documents';

export default function useDocumentTerminal(doc: Doc) {
const ctx = useAppContext();
const [state, init] = useAsync(async () => initState(ctx, doc));
const workspaceDocumentsService = useWorkspaceDocumentsService();
const [state, init] = useAsync(async () =>
initState(ctx, workspaceDocumentsService, doc)
);

useEffect(() => {
init();
Expand All @@ -35,7 +40,11 @@ export default function useDocumentTerminal(doc: Doc) {
return state;
}

async function initState(ctx: IAppContext, doc: Doc) {
async function initState(
ctx: IAppContext,
docsService: DocumentsService,
doc: Doc
) {
const cmd = createCmd(doc);
const ptyProcess = ctx.terminalsService.createPtyProcess(cmd);
const openContextMenu = () => ctx.mainProcessClient.openTerminalContextMenu();
Expand All @@ -46,16 +55,16 @@ async function initState(ctx: IAppContext, doc: Doc) {
}

const cwd = await ptyProcess.getCwd();
ctx.docsService.update(doc.uri, { cwd, title: cwd });
docsService.update(doc.uri, { cwd, title: cwd });
};

ptyProcess.onOpen(() => {
ctx.docsService.update(doc.uri, { status: 'connected' });
docsService.update(doc.uri, { status: 'connected' });
refreshTitle();
});

ptyProcess.onExit(() => {
ctx.docsService.close(doc.uri);
docsService.close(doc.uri);
});

return {
Expand Down
6 changes: 4 additions & 2 deletions web/packages/teleterm/src/ui/DocumentTerminal/useReconnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ limitations under the License.
*/

import { useAppContext } from 'teleterm/ui/appContextProvider';
import * as types from 'teleterm/ui/services/docs/types';
import * as types from 'teleterm/ui/services/workspacesService';
import useAttempt from 'shared/hooks/useAttemptNext';
import { useWorkspaceDocumentsService } from 'teleterm/ui/Documents';

export function useReconnect(doc: types.DocumentTshNode) {
const ctx = useAppContext();
const workspaceDocumentsService = useWorkspaceDocumentsService();
const { attempt, setAttempt } = useAttempt('');

function updateDoc() {
ctx.docsService.update(doc.uri, { status: 'connected' });
workspaceDocumentsService.update(doc.uri, { status: 'connected' });
}

function reconnect() {
Expand Down
64 changes: 64 additions & 0 deletions web/packages/teleterm/src/ui/Documents/DocumentsRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { useAppContext } from 'teleterm/ui/appContextProvider';
import * as types from 'teleterm/ui/services/workspacesService';
import DocumentCluster from 'teleterm/ui/DocumentCluster';
import DocumentGateway from 'teleterm/ui/DocumentGateway';
import DocumentTerminal from 'teleterm/ui/DocumentTerminal';
import Document from 'teleterm/ui/Document';
import { DocumentsService } from 'teleterm/ui/services/workspacesService';
import styled from 'styled-components';
import { WorkspaceDocumentsServiceProvider } from './workspaceDocumentsServiceContext';

export function DocumentsRenderer() {
const { workspacesService } = useAppContext();

function renderDocuments(documentsService: DocumentsService) {
return documentsService.getDocuments().map(doc => {
const isActiveDoc = doc === documentsService.getActive();
return <MemoizedDocument doc={doc} visible={isActiveDoc} key={doc.uri} />;
});
}

return (
<>
{workspacesService
.getWorkspacesDocumentsServices()
.map(({ clusterUri, workspaceDocumentsService }) => (
<DocumentsContainer
isVisible={clusterUri === workspacesService.getRootClusterUri()}
key={clusterUri}
>
<WorkspaceDocumentsServiceProvider value={workspaceDocumentsService}>
{renderDocuments(workspaceDocumentsService)}
</WorkspaceDocumentsServiceProvider>
</DocumentsContainer>
))}
</>
);
}

const DocumentsContainer = styled.div`
display: ${props => (props.isVisible ? 'contents' : 'none')};
`;

function MemoizedDocument(props: { doc: types.Document; visible: boolean }) {
const { doc, visible } = props;
return React.useMemo(() => {
switch (doc.kind) {
case 'doc.cluster':
return <DocumentCluster doc={doc} visible={visible} />;
case 'doc.gateway':
return <DocumentGateway doc={doc} visible={visible} />;
case 'doc.terminal_shell':
case 'doc.terminal_tsh_node':
case 'doc.terminal_tsh_kube':
return <DocumentTerminal doc={doc} visible={visible} />;
default:
return (
<Document visible={visible}>
Document kind "{doc.kind}" is not supported
</Document>
);
}
}, [visible, doc]);
}
2 changes: 2 additions & 0 deletions web/packages/teleterm/src/ui/Documents/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './DocumentsRenderer';
export * from './workspaceDocumentsServiceContext';
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import { DocumentsService } from 'teleterm/ui/services/workspacesService';

const WorkspaceDocumentsServiceContext =
React.createContext<DocumentsService>(null);

export const WorkspaceDocumentsServiceProvider: React.FC<{
value: DocumentsService;
}> = props => {
return <WorkspaceDocumentsServiceContext.Provider {...props} />;
};

export const useWorkspaceDocumentsService = () => {
return React.useContext(WorkspaceDocumentsServiceContext);
};
31 changes: 31 additions & 0 deletions web/packages/teleterm/src/ui/Identity/ClusterItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { ListItem } from 'teleterm/ui/Navigator/NavItem';
import { Flex, Text } from 'design';
import { ListAddCheck } from 'design/Icon';
import styled from 'styled-components';
import LinearProgress from 'teleterm/ui/components/LinearProgress';

interface ClusterItemProps {
isActive: boolean;
title: string;
onClick(): void;
syncing: boolean;
}

export function ClusterItem(props: ClusterItemProps) {
return (
<ListItem onClick={props.onClick}>
<Flex justifyContent="space-between" alignItems="center" width="100%">
<Text typography="body1" title={props.title}>
{props.title}
</Text>
{props.isActive ? <ActiveCheck /> : null}
{props.syncing && <LinearProgress />}
</Flex>
</ListItem>
);
}

const ActiveCheck = styled(ListAddCheck)`
color: ${props => props.theme.colors.progressBarColor};
`;
Loading

0 comments on commit 86ad396

Please sign in to comment.