From 73b3e2dbe82bd2e8faf0ffc84df4ddee57e9dac2 Mon Sep 17 00:00:00 2001 From: Kelvin Schoofs Date: Mon, 21 Dec 2020 16:16:16 +0100 Subject: [PATCH] Refactor webview a bit --- src/connect.ts | 4 +-- src/manager.ts | 2 +- src/{settings.ts => webview.ts} | 36 ++++++++++++++----- src/webviewMessages.ts | 2 +- webview/src/ConfigEditor/configGroupField.tsx | 6 ++-- webview/src/ConfigEditor/fields.tsx | 14 ++++---- webview/src/ConfigEditor/index.tsx | 12 +++---- webview/src/ConfigEditor/proxyFields.tsx | 10 +++--- webview/src/ConfigList/index.tsx | 6 ++-- .../index.tsx => ConfigLocator.tsx} | 9 +++-- webview/src/ConfigLocator/index.css | 4 --- webview/src/FieldTypes/group.tsx | 2 +- webview/src/FieldTypes/path.tsx | 2 +- webview/src/Homescreen/index.css | 4 --- .../{NewConfig/index.tsx => NewConfig.tsx} | 19 +++++----- webview/src/NewConfig/index.css | 4 --- .../{Homescreen/index.tsx => Startscreen.tsx} | 19 +++++----- webview/src/data/actions.ts | 2 +- webview/src/data/index.ts | 6 ++-- webview/src/data/state.ts | 2 +- webview/src/modules.d.ts | 8 ----- webview/src/redux.ts | 33 +++-------------- webview/src/router.tsx | 4 +-- webview/src/tests/redux.tsx | 8 ++--- webview/src/types/fileSystemConfig.ts | 14 ++++---- webview/src/types/webviewMessages.ts | 4 +-- webview/src/view/actions.ts | 2 +- webview/src/view/index.ts | 7 ++-- webview/src/view/reducers.ts | 14 +++++--- webview/src/view/state.ts | 2 +- webview/src/vscode.ts | 6 ++-- webview/tsconfig.json | 1 - webview/tslint.json | 6 +++- 33 files changed, 129 insertions(+), 145 deletions(-) rename src/{settings.ts => webview.ts} (80%) rename webview/src/{ConfigLocator/index.tsx => ConfigLocator.tsx} (73%) delete mode 100644 webview/src/ConfigLocator/index.css delete mode 100644 webview/src/Homescreen/index.css rename webview/src/{NewConfig/index.tsx => NewConfig.tsx} (86%) delete mode 100644 webview/src/NewConfig/index.css rename webview/src/{Homescreen/index.tsx => Startscreen.tsx} (78%) delete mode 100644 webview/src/modules.d.ts diff --git a/src/connect.ts b/src/connect.ts index bc90128..fc624b1 100644 --- a/src/connect.ts +++ b/src/connect.ts @@ -6,7 +6,6 @@ import * as vscode from 'vscode'; import { getConfigs } from './config'; import type { FileSystemConfig } from './fileSystemConfig'; import { censorConfig, Logging } from './logging'; -import { navigate } from './settings'; import { toPromise } from './toPromise'; // tslint:disable-next-line:variable-name @@ -131,7 +130,8 @@ export async function calculateActualConfig(config: FileSystemConfig): Promise { body = body.replace(/\$WEBVIEW_CSPEXTRA/g, `connect-src ${URL} ${URL.replace('http://', 'ws://')};`); cb(null, body); }).on('error', err => { - console.warn('Error connecting to React dev server:', err); + Logging.warning(`Error connecting to React dev server: ${err}`); cb(new Error('Could not connect to React dev server. Not running?')); })); } export async function open() { if (!webviewPanel) { - webviewPanel = vscode.window.createWebviewPanel('sshfs-settings', 'SSH-FS Settings', vscode.ViewColumn.One, { enableFindWidget: true, enableScripts: true }); + const extensionPath = getExtensionPath(); + webviewPanel = vscode.window.createWebviewPanel('sshfs-settings', 'SSH-FS', vscode.ViewColumn.One, { enableFindWidget: true, enableScripts: true }); webviewPanel.onDidDispose(() => webviewPanel = undefined); + if (extensionPath) webviewPanel.iconPath = vscode.Uri.file(path.join(extensionPath, 'resources/icon.svg')); const { webview } = webviewPanel; webview.onDidReceiveMessage(handleMessage); let content = await getDebugContent().catch((e: Error) => (vscode.window.showErrorMessage(e.message), null)); if (!content) { - const extensionPath = getExtensionPath(); if (!extensionPath) throw new Error('Could not get extensionPath'); // If we got here, we're either not in debug mode, or something went wrong (and an error message is shown) content = fs.readFileSync(path.resolve(extensionPath, 'webview/build/index.html')).toString(); @@ -63,19 +66,19 @@ export async function open() { } export async function navigate(navigation: Navigation) { + Logging.debug(`Navigation requested: ${JSON.stringify(navigation, null, 4)}`); pendingNavigation = navigation; postMessage({ navigation, type: 'navigate' }); return open(); } function postMessage(message: T) { - if (!webviewPanel) return; - webviewPanel.webview.postMessage(message); + webviewPanel?.webview.postMessage(message); } async function handleMessage(message: Message): Promise { - console.log('Got message:', message); - if (message.type === 'navigated') pendingNavigation = undefined; + if (!webviewPanel) return Logging.warning(`Got message without webviewPanel: ${JSON.stringify(message, null, 4)}`); + Logging.debug(`Got message: ${JSON.stringify(message, null, 4)}`); if (pendingNavigation) { postMessage({ type: 'navigate', @@ -131,5 +134,22 @@ async function handleMessage(message: Message): Promise { type: 'promptPathResult', }); } + case 'navigated': { + const { view } = message; + type View = 'startscreen' | 'newconfig' | 'configeditor' | 'configlocator'; + let title: string | undefined; + switch (view as View) { + case 'configeditor': + title = 'SSH FS - Edit config'; + break; + case 'configlocator': + title = 'SSH FS - Locate config'; + break; + case 'newconfig': + title = 'SSH FS - New config'; + break; + } + webviewPanel.title = title || 'SSH FS'; + } } } diff --git a/src/webviewMessages.ts b/src/webviewMessages.ts index e402cd5..a685b8f 100644 --- a/src/webviewMessages.ts +++ b/src/webviewMessages.ts @@ -42,7 +42,7 @@ export interface NavigateMessage { } export interface NavigatedMessage { type: 'navigated'; - navigation: Navigation; + view: string; } export interface MessageTypes { diff --git a/webview/src/ConfigEditor/configGroupField.tsx b/webview/src/ConfigEditor/configGroupField.tsx index 9d8fb5f..f1653dc 100644 --- a/webview/src/ConfigEditor/configGroupField.tsx +++ b/webview/src/ConfigEditor/configGroupField.tsx @@ -1,6 +1,6 @@ -import { FieldDropdownWithInput } from 'src/FieldTypes/dropdownwithinput'; -import { connect } from 'src/redux'; -import { getGroups } from 'src/types/fileSystemConfig'; +import { FieldDropdownWithInput } from '../FieldTypes/dropdownwithinput'; +import { connect } from '../redux'; +import { getGroups } from '../types/fileSystemConfig'; export interface StateProps { values: string[]; diff --git a/webview/src/ConfigEditor/fields.tsx b/webview/src/ConfigEditor/fields.tsx index f1758c1..0ad6a85 100644 --- a/webview/src/ConfigEditor/fields.tsx +++ b/webview/src/ConfigEditor/fields.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { FieldDropdown } from 'src/FieldTypes/dropdown'; -import { FieldDropdownWithInput } from 'src/FieldTypes/dropdownwithinput'; -import { FieldNumber } from 'src/FieldTypes/number'; -import { FieldPath } from 'src/FieldTypes/path'; -import { FieldString } from 'src/FieldTypes/string'; -import { FileSystemConfig, invalidConfigName } from 'src/types/fileSystemConfig'; +import { FieldDropdown } from '../FieldTypes/dropdown'; +import { FieldDropdownWithInput } from '../FieldTypes/dropdownwithinput'; +import { FieldNumber } from '../FieldTypes/number'; +import { FieldPath } from '../FieldTypes/path'; +import { FieldString } from '../FieldTypes/string'; +import { FileSystemConfig, invalidConfigName } from '../types/fileSystemConfig'; import FieldConfigGroup from './configGroupField'; import { PROXY_FIELD } from './proxyFields'; @@ -113,7 +113,7 @@ export function passphrase(config: FileSystemConfig, onChange: FSCChanged<'passp export function sftpCommand(config: FileSystemConfig, onChange: FSCChanged<'sftpCommand'>): React.ReactElement { const callback = (newValue?: string) => onChange('sftpCommand', newValue); const description = 'A command to run on the remote SSH session to start a SFTP session (defaults to sftp subsystem)'; - return + return } export function sftpSudo(config: FileSystemConfig, onChange: FSCChanged<'sftpSudo'>): React.ReactElement { diff --git a/webview/src/ConfigEditor/index.tsx b/webview/src/ConfigEditor/index.tsx index c261d5a..fb1090a 100644 --- a/webview/src/ConfigEditor/index.tsx +++ b/webview/src/ConfigEditor/index.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { FieldGroup } from 'src/FieldTypes/group'; -import { connect, pickProperties } from 'src/redux'; -import { FileSystemConfig, formatConfigLocation } from 'src/types/fileSystemConfig'; -import { IConfigEditorState } from 'src/view'; -import { configEditorSetNewConfig, configEditorSetStatusMessage, openStartScreen } from 'src/view/actions'; -import { deleteConfig, saveConfig } from 'src/vscode'; +import { FieldGroup } from '../FieldTypes/group'; +import { connect, pickProperties } from '../redux'; +import { FileSystemConfig, formatConfigLocation } from '../types/fileSystemConfig'; +import type { IConfigEditorState } from '../view'; +import { configEditorSetNewConfig, configEditorSetStatusMessage, openStartScreen } from '../view/actions'; +import { deleteConfig, saveConfig } from '../vscode'; import * as Fields from './fields'; import './index.css'; diff --git a/webview/src/ConfigEditor/proxyFields.tsx b/webview/src/ConfigEditor/proxyFields.tsx index 88e7d05..373325b 100644 --- a/webview/src/ConfigEditor/proxyFields.tsx +++ b/webview/src/ConfigEditor/proxyFields.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; -import { FieldDropdown } from 'src/FieldTypes/dropdown'; -import { FieldDropdownWithInput } from 'src/FieldTypes/dropdownwithinput'; -import { FieldNumber } from 'src/FieldTypes/number'; -import { FieldString } from 'src/FieldTypes/string'; -import { FileSystemConfig } from 'src/types/fileSystemConfig'; +import { FieldDropdown } from '../FieldTypes/dropdown'; +import { FieldDropdownWithInput } from '../FieldTypes/dropdownwithinput'; +import { FieldNumber } from '../FieldTypes/number'; +import { FieldString } from '../FieldTypes/string'; +import { FileSystemConfig } from '../types/fileSystemConfig'; import { FieldFactory, FSCChanged, FSCChangedMultiple } from './fields'; export function proxy(config: FileSystemConfig, onChange: FSCChanged<'proxy'>): React.ReactElement { diff --git a/webview/src/ConfigList/index.tsx b/webview/src/ConfigList/index.tsx index 3bc5032..4a3909e 100644 --- a/webview/src/ConfigList/index.tsx +++ b/webview/src/ConfigList/index.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; -import { connect } from 'src/redux'; -import { FileSystemConfig } from 'src/types/fileSystemConfig'; -import { openConfigEditor } from 'src/view/actions'; +import { connect } from '../redux'; +import type { FileSystemConfig } from '../types/fileSystemConfig'; +import { openConfigEditor } from '../view/actions'; import './index.css'; interface StateProps { diff --git a/webview/src/ConfigLocator/index.tsx b/webview/src/ConfigLocator.tsx similarity index 73% rename from webview/src/ConfigLocator/index.tsx rename to webview/src/ConfigLocator.tsx index 6ee78ac..3a72301 100644 --- a/webview/src/ConfigLocator/index.tsx +++ b/webview/src/ConfigLocator.tsx @@ -1,9 +1,8 @@ import * as React from 'react'; -import ConfigList from 'src/ConfigList'; -import { connect, pickProperties } from 'src/redux'; -import { FileSystemConfig, formatConfigLocation } from 'src/types/fileSystemConfig'; -import { IConfigLocatorState } from 'src/view'; -import './index.css'; +import ConfigList from './ConfigList'; +import { connect, pickProperties } from './redux'; +import { FileSystemConfig, formatConfigLocation } from './types/fileSystemConfig'; +import type { IConfigLocatorState } from './view'; function displayName(config: FileSystemConfig) { return formatConfigLocation(config._location); diff --git a/webview/src/ConfigLocator/index.css b/webview/src/ConfigLocator/index.css deleted file mode 100644 index b3553f3..0000000 --- a/webview/src/ConfigLocator/index.css +++ /dev/null @@ -1,4 +0,0 @@ - -div.ConfigLocator { - padding: 5px; -} diff --git a/webview/src/FieldTypes/group.tsx b/webview/src/FieldTypes/group.tsx index 36d7a8a..6c3a630 100644 --- a/webview/src/FieldTypes/group.tsx +++ b/webview/src/FieldTypes/group.tsx @@ -6,7 +6,7 @@ const CONTEXT = React.createContext(undefined); export class FieldGroup extends React.Component { public static Consumer = CONTEXT.Consumer; protected static CONTEXT = CONTEXT; - protected fields: Array> = []; + protected fields: FieldBase[] = []; public register(field: FieldBase) { this.fields.push(field); } diff --git a/webview/src/FieldTypes/path.tsx b/webview/src/FieldTypes/path.tsx index 2aeeed9..1ece309 100644 --- a/webview/src/FieldTypes/path.tsx +++ b/webview/src/FieldTypes/path.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { promptPath } from 'src/vscode'; +import { promptPath } from '../vscode'; import { FieldBase } from './base'; export class FieldPath extends FieldBase { diff --git a/webview/src/Homescreen/index.css b/webview/src/Homescreen/index.css deleted file mode 100644 index 924a0ad..0000000 --- a/webview/src/Homescreen/index.css +++ /dev/null @@ -1,4 +0,0 @@ - -div.Homescreen { - padding: 5px; -} diff --git a/webview/src/NewConfig/index.tsx b/webview/src/NewConfig.tsx similarity index 86% rename from webview/src/NewConfig/index.tsx rename to webview/src/NewConfig.tsx index 0b3d92d..1c9f0fb 100644 --- a/webview/src/NewConfig/index.tsx +++ b/webview/src/NewConfig.tsx @@ -1,13 +1,12 @@ import * as React from 'react'; -import { FieldDropdown } from 'src/FieldTypes/dropdown'; -import { FieldGroup } from 'src/FieldTypes/group'; -import { FieldString } from 'src/FieldTypes/string'; -import { connect, pickProperties, State } from 'src/redux'; -import { ConfigLocation, formatConfigLocation, invalidConfigName } from 'src/types/fileSystemConfig'; -import { INewConfigState } from 'src/view'; -import { newConfigSetLocation, newConfigSetName, openConfigEditor, openStartScreen } from 'src/view/actions'; -import { createConfig } from 'src/vscode'; -import './index.css'; +import { FieldDropdown } from './FieldTypes/dropdown'; +import { FieldGroup } from './FieldTypes/group'; +import { FieldString } from './FieldTypes/string'; +import { connect, pickProperties, State } from './redux'; +import { ConfigLocation, formatConfigLocation, invalidConfigName } from './types/fileSystemConfig'; +import type { INewConfigState } from './view'; +import { newConfigSetLocation, newConfigSetName, openConfigEditor, openStartScreen } from './view/actions'; +import { createConfig } from './vscode'; const LOCATION_DESCRIPTION = 'The file or Settings file to add the new configuration to'; @@ -55,7 +54,7 @@ class NewConfig extends React.Component { interface SubState extends State { view: INewConfigState } export default connect(NewConfig)( - (state) => ({ + (state) => ({ ...pickProperties(state.view, 'name'), ...pickProperties(state.data, 'locations'), location: state.view.location || state.data.locations[0], diff --git a/webview/src/NewConfig/index.css b/webview/src/NewConfig/index.css deleted file mode 100644 index 924a0ad..0000000 --- a/webview/src/NewConfig/index.css +++ /dev/null @@ -1,4 +0,0 @@ - -div.Homescreen { - padding: 5px; -} diff --git a/webview/src/Homescreen/index.tsx b/webview/src/Startscreen.tsx similarity index 78% rename from webview/src/Homescreen/index.tsx rename to webview/src/Startscreen.tsx index d35af9a..9c9e71b 100644 --- a/webview/src/Homescreen/index.tsx +++ b/webview/src/Startscreen.tsx @@ -1,12 +1,11 @@ import * as React from 'react'; -import ConfigList from 'src/ConfigList'; -import { receivedData } from 'src/data/actions'; -import { connect, pickProperties } from 'src/redux'; -import { ConfigLocation, FileSystemConfig, formatConfigLocation, groupByGroup, groupByLocation } from 'src/types/fileSystemConfig'; -import { IStartScreenState } from 'src/view'; -import { openNewConfig, openStartScreen } from 'src/view/actions'; -import { API } from 'src/vscode'; -import './index.css'; +import ConfigList from './ConfigList'; +import { receivedData } from './data/actions'; +import { connect, pickProperties } from './redux'; +import { ConfigLocation, FileSystemConfig, formatConfigLocation, groupByGroup, groupByLocation } from './types/fileSystemConfig'; +import { IStartScreenState } from './view'; +import { openNewConfig, openStartScreen } from './view/actions'; +import { API } from './vscode'; interface StateProps { configs: FileSystemConfig[]; @@ -17,7 +16,7 @@ interface DispatchProps { changeGroupBy(current: string): void; add(): void; } -class Homescreen extends React.Component { +class Startscreen extends React.Component { public componentDidMount() { this.props.refresh(); } @@ -43,7 +42,7 @@ class Homescreen extends React.Component { public changeGroupBy = () => this.props.changeGroupBy(this.props.groupBy); } -export default connect(Homescreen)( +export default connect(Startscreen)( state => ({ ...pickProperties(state.data, 'configs'), ...pickProperties(state.view as IStartScreenState, 'groupBy'), diff --git a/webview/src/data/actions.ts b/webview/src/data/actions.ts index b9b7194..9d331aa 100644 --- a/webview/src/data/actions.ts +++ b/webview/src/data/actions.ts @@ -1,4 +1,4 @@ -import { ConfigLocation, FileSystemConfig } from 'src/types/fileSystemConfig'; +import type { ConfigLocation, FileSystemConfig } from '../types/fileSystemConfig'; export enum ActionType { RECEIVED_DATA = 'RECEIVED_DATA', diff --git a/webview/src/data/index.ts b/webview/src/data/index.ts index fac9bc0..ad48b1a 100644 --- a/webview/src/data/index.ts +++ b/webview/src/data/index.ts @@ -1,11 +1,11 @@ -import { Store } from 'redux'; -import { addListener } from 'src/vscode'; +import type { Store } from 'redux'; +import { addListener } from '../vscode'; import * as actions from './actions'; export { reducer } from './reducers'; -export { actions } export * from './state'; +export { actions }; export function initStore(store: Store) { addListener((msg) => store.dispatch(actions.receivedData(msg.configs, msg.locations)), 'responseData'); diff --git a/webview/src/data/state.ts b/webview/src/data/state.ts index 4e8ff92..64e8847 100644 --- a/webview/src/data/state.ts +++ b/webview/src/data/state.ts @@ -1,4 +1,4 @@ -import { ConfigLocation, FileSystemConfig } from 'src/types/fileSystemConfig'; +import type { ConfigLocation, FileSystemConfig } from '../types/fileSystemConfig'; export interface IState { configs: FileSystemConfig[]; diff --git a/webview/src/modules.d.ts b/webview/src/modules.d.ts deleted file mode 100644 index d47c4e5..0000000 --- a/webview/src/modules.d.ts +++ /dev/null @@ -1,8 +0,0 @@ - -declare module '*.svg' -declare module '*.png' -declare module '*.jpg' -declare module '*.jpeg' -declare module '*.gif' -declare module '*.bmp' -declare module '*.tiff' diff --git a/webview/src/redux.ts b/webview/src/redux.ts index 1c74a6c..eaf4be8 100644 --- a/webview/src/redux.ts +++ b/webview/src/redux.ts @@ -35,40 +35,17 @@ STORE.dispatch = (action) => { STORE.subscribe(() => API.setState(STORE.getState())); +API.postMessage({ type: 'navigated', view: STORE.getState().view.view }); + // Makes debugging easier (and this is inside our WebView context anyway) (window as any).STORE = STORE; -// type GetComponentProps = C extends React.ComponentClass ? P : never; type GetComponentProps = C extends React.ComponentClass ? P : (C extends React.FunctionComponent ? P2 : {}); type GetComponentState = C extends React.ComponentClass ? S : {}; type Omit = Pick>; -// type ConnectReturn = (c: C) => React.ComponentClass & TProps, keyof TProps>, GetComponentState>; - -/* -export function connect(stateToProps: (state: TState) => TStateProps, dispatchToProps?: (dispatch: Dispatch) => TDispatchProps) { - return realConnect(stateToProps, dispatchToProps) as ConnectReturn; -} -*/ - -/* -export function connect | React.FunctionComponent), TStateProps, TDispatchProps = {}, TState = State>( - component: TComponent, - stateToProps: (state: TState) => TStateProps, - dispatchToProps?: (dispatch: Dispatch, ownProps: Omit, keyof TStateProps & TDispatchProps>) => TDispatchProps, -): React.ComponentClass & TStateProps & TDispatchProps, keyof TStateProps & TDispatchProps>, GetComponentState> { - return realConnect(stateToProps, dispatchToProps)(component) as any; -} -*/ - -/* -type ConnectReturn | React.FunctionComponent)> = ( - stateToProps: (state: TState) => TStateProps, - dispatchToProps?: (dispatch: Dispatch, ownProps: Omit, keyof TStateProps & TDispatchProps>) => TDispatchProps, -) => React.ComponentClass & TStateProps & TDispatchProps, keyof TStateProps & TDispatchProps>, GetComponentState>; -*/ -type OwnProps | React.FunctionComponent),P> = Omit, keyof P>; +type OwnProps | React.FunctionComponent), P> = Omit, keyof P>; interface ConnectReturn | React.FunctionComponent)> { ( @@ -84,8 +61,8 @@ export function connect | React.Fu return (stateToProps: any, dispatchToProps?: any) => realConnect(stateToProps, dispatchToProps)(component) as any; } -export function pickProperties(obj: T, ...keys: K[]): Pick { - const res: Pick = {} as any; +export function pickProperties(obj: T, ...keys: K[]): Pick { + const res: Pick = {} as any; for (const key of keys) { res[key] = obj[key]; } diff --git a/webview/src/router.tsx b/webview/src/router.tsx index c17669a..5418900 100644 --- a/webview/src/router.tsx +++ b/webview/src/router.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import ConfigEditor from './ConfigEditor'; import ConfigLocator from './ConfigLocator'; -import Homescreen from './Homescreen'; import NewConfig from './NewConfig'; import { connect, State } from './redux'; +import Startscreen from './Startscreen'; interface StateProps { view: State['view']['view']; @@ -18,7 +18,7 @@ function Router(props: StateProps) { return ; case 'startscreen': default: - return ; + return ; } } diff --git a/webview/src/tests/redux.tsx b/webview/src/tests/redux.tsx index 345a4c9..0fa5fdf 100644 --- a/webview/src/tests/redux.tsx +++ b/webview/src/tests/redux.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { connect } from "src/redux"; +import { connect } from "../redux"; function nop(...args: any) { return; @@ -15,10 +15,10 @@ function nop(...args: any) { class Test extends React.Component { } const TestC1 = connect(Test)(state => ({ s: 123 })); nop(); - const TestC2 = connect(Test)(state => ({ s: 123 }), dispatch => ({ d: 456 })); + const TestC2 = connect(Test)(state => ({ s: 123 }), dispatch => ({ d: 456 })); nop(); - const TestC3 = connect(Test)(state => ({ s: 123 })); + const TestC3 = connect(Test)(state => ({ s: 123 })); nop(); - const TestC4 = connect(Test)(state => ({ s: 123 }), dispatch => ({ d: 456 })); + const TestC4 = connect(Test)(state => ({ s: 123 }), dispatch => ({ d: 456 })); nop(); } diff --git a/webview/src/types/fileSystemConfig.ts b/webview/src/types/fileSystemConfig.ts index 55eaaff..35b5dfd 100644 --- a/webview/src/types/fileSystemConfig.ts +++ b/webview/src/types/fileSystemConfig.ts @@ -1,4 +1,4 @@ -import { ConnectConfig } from 'ssh2'; +import type { ConnectConfig } from 'ssh2'; export interface ProxyConfig { type: 'socks4' | 'socks5' | 'http'; @@ -43,8 +43,8 @@ export function getGroups(configs: FileSystemConfig[], expanded = false): string return res; } -export function groupByLocation(configs: FileSystemConfig[]): Array<[ConfigLocation, FileSystemConfig[]]> { - const res: Array<[ConfigLocation, FileSystemConfig[]]> = []; +export function groupByLocation(configs: FileSystemConfig[]): [ConfigLocation, FileSystemConfig[]][] { + const res: [ConfigLocation, FileSystemConfig[]][] = []; function getForLoc(loc: ConfigLocation = 'Unknown') { let found = res.find(([l]) => l === loc); if (found) return found; @@ -58,8 +58,8 @@ export function groupByLocation(configs: FileSystemConfig[]): Array<[ConfigLocat return res; } -export function groupByGroup(configs: FileSystemConfig[]): Array<[string, FileSystemConfig[]]> { - const res: Array<[string, FileSystemConfig[]]> = []; +export function groupByGroup(configs: FileSystemConfig[]): [string, FileSystemConfig[]][] { + const res: [string, FileSystemConfig[]][] = []; function getForGroup(group: string = '') { let found = res.find(([l]) => l === group); if (found) return found; @@ -92,7 +92,7 @@ export interface FileSystemConfig extends ConnectConfig { privateKeyPath?: string; /** A name of another config to use as a hop */ hop?: string; - /** A command to run on the remote SSH session to start a SFTP session (defaults to sftp subsystem) */ + /** The command to run on the remote SSH session to start a SFTP session (defaults to sftp subsystem) */ sftpCommand?: string; /** Whether to use a sudo shell (and for which user) to run the sftpCommand in (sftpCommand defaults to /usr/lib/openssh/sftp-server if missing) */ sftpSudo?: string | boolean; @@ -104,6 +104,8 @@ export interface FileSystemConfig extends ConnectConfig { _location?: ConfigLocation; /** Internal property keeping track of where this config comes from (including merges) */ _locations: ConfigLocation[]; + /** Internal property keeping track of whether this config is an actually calculated one, and if so, which config it originates from (normally itself) */ + _calculated?: FileSystemConfig; } export function invalidConfigName(name: string) { diff --git a/webview/src/types/webviewMessages.ts b/webview/src/types/webviewMessages.ts index 3cf4848..a685b8f 100644 --- a/webview/src/types/webviewMessages.ts +++ b/webview/src/types/webviewMessages.ts @@ -1,4 +1,4 @@ -import { ConfigLocation, FileSystemConfig } from './fileSystemConfig'; +import type { ConfigLocation, FileSystemConfig } from './fileSystemConfig'; /* Type of messages*/ @@ -42,7 +42,7 @@ export interface NavigateMessage { } export interface NavigatedMessage { type: 'navigated'; - navigation: Navigation; + view: string; } export interface MessageTypes { diff --git a/webview/src/view/actions.ts b/webview/src/view/actions.ts index 255bace..c495b0f 100644 --- a/webview/src/view/actions.ts +++ b/webview/src/view/actions.ts @@ -1,4 +1,4 @@ -import { ConfigLocation, FileSystemConfig } from 'src/types/fileSystemConfig'; +import type { ConfigLocation, FileSystemConfig } from '../types/fileSystemConfig'; export enum ActionType { // Startscreen diff --git a/webview/src/view/index.ts b/webview/src/view/index.ts index 6d5b495..30e65e5 100644 --- a/webview/src/view/index.ts +++ b/webview/src/view/index.ts @@ -1,11 +1,11 @@ -import { Store } from 'redux'; -import { addListener, API } from 'src/vscode'; +import type { Store } from 'redux'; +import { addListener } from '../vscode'; import * as actions from './actions'; export { reducer } from './reducers'; -export { actions } export * from './state'; +export { actions }; export function initStore(store: Store) { addListener((msg) => { @@ -24,6 +24,5 @@ export function initStore(store: Store) { return store.dispatch(actions.openConfigEditor(config)); } } - API.postMessage({ type: 'navigated', navigation }); }, 'navigate'); } diff --git a/webview/src/view/reducers.ts b/webview/src/view/reducers.ts index 74a0d9d..51e1ed4 100644 --- a/webview/src/view/reducers.ts +++ b/webview/src/view/reducers.ts @@ -1,17 +1,23 @@ +import { API } from '../vscode'; import { Action, ActionType } from './actions'; import { DEFAULT_STATE, IConfigEditorState, INewConfigState, IStartScreenState, IState } from './state'; +function setView(state: IState): IState { + API.postMessage({ type: 'navigated', view: state.view }); + return state; +} + export function reducer(state = DEFAULT_STATE, action: Action): IState { switch (action.type) { // Startscreen case ActionType.OPEN_STARTSCREEN: { const groupBy = action.groupBy || (state as IStartScreenState).groupBy || 'group'; - return { ...state, view: 'startscreen', groupBy }; + return setView({ ...state, view: 'startscreen', groupBy }); } // New Config case ActionType.OPEN_NEWCONFIG: { const { name } = action; - return { ...state, view: 'newconfig', name, location: undefined }; + return setView({ ...state, view: 'newconfig', name, location: undefined }); } case ActionType.NEWCONFIG_SETNAME: return { ...state as INewConfigState, name: action.name }; @@ -20,11 +26,11 @@ export function reducer(state = DEFAULT_STATE, action: Action): IState { // ConfigEditor case ActionType.OPEN_CONFIGEDITOR: { const { config } = action; - return { ...state, view: 'configeditor', oldConfig: config, newConfig: config }; + return setView({ ...state, view: 'configeditor', oldConfig: config, newConfig: config }); } case ActionType.OPEN_CONFIGLOCATOR: { const { name, configs } = action; - return { ...state, view: 'configlocator', name, configs }; + return setView({ ...state, view: 'configlocator', name, configs }); } case ActionType.CONFIGEDITOR_SETNEWCONFIG: return { ...state as IConfigEditorState, newConfig: action.config }; diff --git a/webview/src/view/state.ts b/webview/src/view/state.ts index 59e70c7..f23e81b 100644 --- a/webview/src/view/state.ts +++ b/webview/src/view/state.ts @@ -1,4 +1,4 @@ -import { ConfigLocation, FileSystemConfig } from 'src/types/fileSystemConfig'; +import type { ConfigLocation, FileSystemConfig } from '../types/fileSystemConfig'; interface IViewState { view: V; diff --git a/webview/src/vscode.ts b/webview/src/vscode.ts index a9e4740..7a3811b 100644 --- a/webview/src/vscode.ts +++ b/webview/src/vscode.ts @@ -1,6 +1,6 @@ -import { Message, MessageTypes, PromptPathResultMessage, SaveConfigResultMessage } from 'src/types/webviewMessages'; -import { ConfigLocation, FileSystemConfig } from './types/fileSystemConfig'; +import type { ConfigLocation, FileSystemConfig } from './types/fileSystemConfig'; +import type { Message, MessageTypes, PromptPathResultMessage, SaveConfigResultMessage } from './types/webviewMessages'; interface VSCodeAPI { postMessage(msg: Message): void; @@ -13,7 +13,7 @@ export const API: VSCodeAPI = acquireVsCodeApi(); export type Listener = (message: T) => void; export type Filter = string | ((message: Message) => boolean); -let LISTENERS: Array<[Listener, Filter | undefined]> = []; +let LISTENERS: [Listener, Filter | undefined][] = []; export function addListener(listener: Listener): void; export function addListener(listener: Listener, filter: K): void; diff --git a/webview/tsconfig.json b/webview/tsconfig.json index d0f44f0..f5f2dc3 100644 --- a/webview/tsconfig.json +++ b/webview/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "baseUrl": ".", "outDir": "build/dist", "module": "esnext", "target": "es5", diff --git a/webview/tslint.json b/webview/tslint.json index f1f53f9..529610f 100644 --- a/webview/tslint.json +++ b/webview/tslint.json @@ -7,7 +7,11 @@ "rules": { "interface-name": false, "curly": false, - "no-console": false + "no-console": false, + "array-type": [ + true, + "array" + ] }, "linterOptions": { "exclude": [