From 1965f51b08ef11308874c419973035479af607ca Mon Sep 17 00:00:00 2001 From: Kelvin Schoofs Date: Thu, 18 Apr 2019 16:38:46 +0200 Subject: [PATCH] Implement group support in Settings UI --- webview/src/ConfigEditor/configGroupField.tsx | 11 ++++++++ webview/src/ConfigEditor/fields.tsx | 9 ++++++- webview/src/Homescreen/index.tsx | 27 +++++++++++++------ webview/src/view/actions.ts | 5 ++-- webview/src/view/reducers.ts | 8 +++--- webview/src/view/state.ts | 5 +++- 6 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 webview/src/ConfigEditor/configGroupField.tsx diff --git a/webview/src/ConfigEditor/configGroupField.tsx b/webview/src/ConfigEditor/configGroupField.tsx new file mode 100644 index 0000000..9d8fb5f --- /dev/null +++ b/webview/src/ConfigEditor/configGroupField.tsx @@ -0,0 +1,11 @@ +import { FieldDropdownWithInput } from 'src/FieldTypes/dropdownwithinput'; +import { connect } from 'src/redux'; +import { getGroups } from 'src/types/fileSystemConfig'; + +export interface StateProps { + values: string[]; +} + +export default connect(FieldDropdownWithInput)( + state => ({ values: getGroups(state.data.configs).sort() }), +); diff --git a/webview/src/ConfigEditor/fields.tsx b/webview/src/ConfigEditor/fields.tsx index d8ec823..2ef0056 100644 --- a/webview/src/ConfigEditor/fields.tsx +++ b/webview/src/ConfigEditor/fields.tsx @@ -5,6 +5,7 @@ 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 FieldConfigGroup from './configGroupField'; import { PROXY_FIELD } from './proxyFields'; export type FieldChanged = (field: K, newValue: V) => void; @@ -38,6 +39,12 @@ export function label(config: FileSystemConfig, onChange: FSCChanged<'label'>): return } +export function group(config: FileSystemConfig, onChange: FSCChanged<'group'>): React.ReactElement { + const callback = (newValue: string) => onChange('group', newValue); + const description = 'Group for this config, to group configs together in some UI places. Allows subgroups, in the format "Group1.SubGroup1.Subgroup2"'; + return +} + export function putty(config: FileSystemConfig, onChange: FSCChanged<'putty'>): React.ReactElement { const callback = (newValue: string) => onChange('putty', newValue === '' ? true : newValue); const description = 'A name of a PuTTY session, or `true` to find the PuTTY session from the host address'; @@ -104,6 +111,6 @@ export function passphrase(config: FileSystemConfig, onChange: FSCChanged<'passp export type FieldFactory = (config: FileSystemConfig, onChange: FSCChanged, onChangeMultiple: FSCChangedMultiple) => React.ReactElement | null; export const FIELDS: FieldFactory[] = [ - name, merge, label, putty, host, port, + name, merge, label, group, putty, host, port, root, agent, username, password, privateKeyPath, passphrase, PROXY_FIELD]; diff --git a/webview/src/Homescreen/index.tsx b/webview/src/Homescreen/index.tsx index c1b5a9d..d35af9a 100644 --- a/webview/src/Homescreen/index.tsx +++ b/webview/src/Homescreen/index.tsx @@ -2,16 +2,19 @@ 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, groupByLocation } from 'src/types/fileSystemConfig'; -import { openNewConfig } from 'src/view/actions'; +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'; interface StateProps { configs: FileSystemConfig[]; + groupBy: string; } interface DispatchProps { refresh(): void; + changeGroupBy(current: string): void; add(): void; } class Homescreen extends React.Component { @@ -19,27 +22,35 @@ class Homescreen extends React.Component { this.props.refresh(); } public render() { - const grouped = groupByLocation(this.props.configs); - grouped.sort(); + const grouped = (this.props.groupBy === 'group' ? groupByGroup : groupByLocation)(this.props.configs); + grouped.sort((a, b) => a[0] > b[0] ? 1 : -1); + const nextGroupBy = this.props.groupBy === 'group' ? 'location' : 'group'; return

Configurations

+ {grouped.map(([loc, configs]) => this.createGroup(loc, configs))}
; } - public createGroup(location: ConfigLocation, configs: FileSystemConfig[]) { - return
-

{formatConfigLocation(location)}

+ public createGroup(group: string | ConfigLocation, configs: FileSystemConfig[]) { + const title = this.props.groupBy === 'group' ? group : formatConfigLocation(group as ConfigLocation); + return
+

{title}

; } + public changeGroupBy = () => this.props.changeGroupBy(this.props.groupBy); } export default connect(Homescreen)( - state => pickProperties(state.data, 'configs'), + state => ({ + ...pickProperties(state.data, 'configs'), + ...pickProperties(state.view as IStartScreenState, 'groupBy'), + }), dispatch => ({ add: () => dispatch(openNewConfig()), + changeGroupBy: (current: string) => dispatch(openStartScreen(current === 'group' ? 'location' : 'group')), refresh: () => (dispatch(receivedData([], [])), API.postMessage({ type: 'requestData' })), }), ); diff --git a/webview/src/view/actions.ts b/webview/src/view/actions.ts index ac2caab..255bace 100644 --- a/webview/src/view/actions.ts +++ b/webview/src/view/actions.ts @@ -37,9 +37,10 @@ interface IAction { export interface IActionOpenStartscreen extends IAction { type: ActionType.OPEN_STARTSCREEN; + groupBy?: string; } -export function openStartScreen(): IActionOpenStartscreen { - return { type: ActionType.OPEN_STARTSCREEN }; +export function openStartScreen(groupBy?: string): IActionOpenStartscreen { + return { type: ActionType.OPEN_STARTSCREEN, groupBy }; } /* NewConfig */ diff --git a/webview/src/view/reducers.ts b/webview/src/view/reducers.ts index 58e4b13..74a0d9d 100644 --- a/webview/src/view/reducers.ts +++ b/webview/src/view/reducers.ts @@ -1,11 +1,13 @@ import { Action, ActionType } from './actions'; -import { DEFAULT_STATE, IConfigEditorState, INewConfigState, IState } from './state'; +import { DEFAULT_STATE, IConfigEditorState, INewConfigState, IStartScreenState, IState } from './state'; export function reducer(state = DEFAULT_STATE, action: Action): IState { switch (action.type) { // Startscreen - case ActionType.OPEN_STARTSCREEN: - return { ...state, view: 'startscreen' }; + case ActionType.OPEN_STARTSCREEN: { + const groupBy = action.groupBy || (state as IStartScreenState).groupBy || 'group'; + return { ...state, view: 'startscreen', groupBy }; + } // New Config case ActionType.OPEN_NEWCONFIG: { const { name } = action; diff --git a/webview/src/view/state.ts b/webview/src/view/state.ts index 6ad9fb4..59e70c7 100644 --- a/webview/src/view/state.ts +++ b/webview/src/view/state.ts @@ -4,7 +4,9 @@ interface IViewState { view: V; } -export type IStartScreenState = IViewState<'startscreen'>; +export interface IStartScreenState extends IViewState<'startscreen'> { + groupBy: string; +} export interface INewConfigState extends IViewState<'newconfig'> { location?: ConfigLocation; @@ -25,5 +27,6 @@ export interface IConfigLocatorState extends IViewState<'configlocator'> { export type IState = IStartScreenState | INewConfigState | IConfigEditorState | IConfigLocatorState; export const DEFAULT_STATE: IState = { + groupBy: 'group', view: 'startscreen', }