From 3efe875bb4e86310b5349f015685fc7852b2f7d5 Mon Sep 17 00:00:00 2001 From: Kirill Shumilov Date: Thu, 4 Feb 2021 18:04:13 +0300 Subject: [PATCH] feat(xod-client): add table log tab, show data, switch sources and sheets, copy, save --- .../src/core/styles/components/Button.scss | 6 + .../styles/components/ReactDatasheet.scss | 3 +- .../src/core/styles/components/TableLog.scss | 93 +++++++ packages/xod-client/src/core/styles/main.scss | 1 + packages/xod-client/src/debugger/actions.js | 4 +- packages/xod-client/src/debugger/constants.js | 2 + packages/xod-client/src/debugger/reducer.js | 3 +- packages/xod-client/src/debugger/selectors.js | 17 ++ packages/xod-client/src/editor/actionTypes.js | 3 + packages/xod-client/src/editor/actions.js | 36 +++ .../src/editor/components/TabtestEditor.jsx | 1 + packages/xod-client/src/editor/constants.js | 2 + .../src/editor/containers/Editor.jsx | 45 +++- .../src/editor/containers/Patch/index.jsx | 1 + .../containers/Patch/modes/debugging.jsx | 2 + .../containers/Patch/modes/selecting.jsx | 2 + .../src/editor/containers/TableLog.jsx | 229 ++++++++++++++++++ packages/xod-client/src/editor/reducer.js | 45 +++- packages/xod-client/src/editor/selectors.js | 46 ++-- 19 files changed, 502 insertions(+), 39 deletions(-) create mode 100644 packages/xod-client/src/core/styles/components/TableLog.scss create mode 100644 packages/xod-client/src/editor/containers/TableLog.jsx diff --git a/packages/xod-client/src/core/styles/components/Button.scss b/packages/xod-client/src/core/styles/components/Button.scss index af4fc70b..75597c9a 100644 --- a/packages/xod-client/src/core/styles/components/Button.scss +++ b/packages/xod-client/src/core/styles/components/Button.scss @@ -52,4 +52,10 @@ box-sizing: border-box; padding: 6px 10px; } + + &--inline { + height: 22px; + line-height: 0; + padding: 0.2rem 0.5rem; + } } diff --git a/packages/xod-client/src/core/styles/components/ReactDatasheet.scss b/packages/xod-client/src/core/styles/components/ReactDatasheet.scss index 2dd3c632..aa9321d9 100644 --- a/packages/xod-client/src/core/styles/components/ReactDatasheet.scss +++ b/packages/xod-client/src/core/styles/components/ReactDatasheet.scss @@ -34,7 +34,6 @@ span.data-grid-container, span.data-grid-container:focus { } .data-grid-container .data-grid .cell.read-only { - background: whitesmoke; color: #999; text-align: center; } @@ -92,7 +91,7 @@ span.data-grid-container, span.data-grid-container:focus { display: block; } -.data-grid-container tr:first-child .cell { +.data-grid.with-header tr:first-child .cell { font-weight: bold; text-align: right; background-color: $grey; diff --git a/packages/xod-client/src/core/styles/components/TableLog.scss b/packages/xod-client/src/core/styles/components/TableLog.scss new file mode 100644 index 00000000..89229b59 --- /dev/null +++ b/packages/xod-client/src/core/styles/components/TableLog.scss @@ -0,0 +1,93 @@ +@mixin header-vertical-centered { + height: 22px; + padding: 8px 0; +} + +.TableLog { + position: relative; + width: 100%; + height: 100%; + + flex: 1; + flex-grow: 1; + flex-flow: column; + + &-header { + height: 40px; + overflow: hidden; + padding: 1px 3px 1px 8px; + + .sourceSelector { + @include header-vertical-centered; + float: left; + margin-right: 1rem; + + label { + color: $chalk; + margin-right: .5rem; + } + + select { + width: 300px; + text-overflow: ellipsis; + } + } + .sheets { + @include header-vertical-centered; + float: left; + margin-left: 1rem; + + .currentSheet { + display: inline-block; + margin: 0 .5rem; + color: $chalk; + } + + button { + height: 22px; + } + } + .actions { + @include header-vertical-centered; + float: right; + margin-left: 1rem; + + button { + margin-left: 2px; + + .fa { + margin-right: .5em; + } + } + } + } + + &-content { + position: absolute; + top: 40px; + bottom: 0; + left: 0; + right: 0; + overflow: scroll; + + background: $grey; + + .no-data { + position: absolute; + top: 50%; + margin-top: -.5em; + + width: 100%; + text-align: center; + + color: $chalk; + font-size: $font-size-l; + } + + .data-grid { + margin-top: -2px; // compensate border-spacing + } + + @include styled-scrollbar(); + } +} diff --git a/packages/xod-client/src/core/styles/main.scss b/packages/xod-client/src/core/styles/main.scss index 0154c9bd..cf2fd638 100644 --- a/packages/xod-client/src/core/styles/main.scss +++ b/packages/xod-client/src/core/styles/main.scss @@ -58,6 +58,7 @@ 'components/SnappingPreviewLayer', 'components/SplitPane', 'components/Suggester', + 'components/TableLog', 'components/Tabs', 'components/TabsContainer', 'components/TabsItem', diff --git a/packages/xod-client/src/debugger/actions.js b/packages/xod-client/src/debugger/actions.js index b6ec1f24..9f602d0f 100644 --- a/packages/xod-client/src/debugger/actions.js +++ b/packages/xod-client/src/debugger/actions.js @@ -36,7 +36,7 @@ export const startDebuggerSession = ( nodePinKeysMap, tableLogNodeIds, pinsAffectedByErrorRaisers, - currentPatchPath, + patchPath, globals, tetheringInetNodeId ) => ({ @@ -47,7 +47,7 @@ export const startDebuggerSession = ( nodePinKeysMap, tableLogNodeIds, pinsAffectedByErrorRaisers, - patchPath: currentPatchPath, + patchPath, globals, tetheringInetNodeId, }, diff --git a/packages/xod-client/src/debugger/constants.js b/packages/xod-client/src/debugger/constants.js index 062f9289..b9631ba8 100644 --- a/packages/xod-client/src/debugger/constants.js +++ b/packages/xod-client/src/debugger/constants.js @@ -24,3 +24,5 @@ export const SESSION_TYPE = { SERIAL: 'serial', SIMULATON: 'simulation', }; + +export const NEW_SHEET = String.fromCharCode(0xc); diff --git a/packages/xod-client/src/debugger/reducer.js b/packages/xod-client/src/debugger/reducer.js index 30f8a571..726fd73e 100644 --- a/packages/xod-client/src/debugger/reducer.js +++ b/packages/xod-client/src/debugger/reducer.js @@ -33,7 +33,7 @@ import { import * as EAT from '../editor/actionTypes'; -import { UPLOAD_MSG_TYPE, LOG_TAB_TYPE, SESSION_TYPE } from './constants'; +import { UPLOAD_MSG_TYPE, LOG_TAB_TYPE, SESSION_TYPE, NEW_SHEET } from './constants'; import * as MSG from './messages'; import { STATUS } from '../utils/constants'; import { isXodErrorMessage } from './debugProtocol'; @@ -157,7 +157,6 @@ const updateInteractiveNodeValues = R.curry((messageList, state) => { )(watchNodeMessages); // Prepare data for table logs - const NEW_SHEET = String.fromCharCode(0xc); // TODO: Move somewhere const newTableLogData = R.map( R.compose( R.reduce( diff --git a/packages/xod-client/src/debugger/selectors.js b/packages/xod-client/src/debugger/selectors.js index a0c408ed..a903d392 100644 --- a/packages/xod-client/src/debugger/selectors.js +++ b/packages/xod-client/src/debugger/selectors.js @@ -296,3 +296,20 @@ export const tetheringInetTransmitter = R.compose( R.path(['tetheringInet', 'transmitter']), getDebuggerState ); + +// ============================================================================= +// +// Table log +// +// ============================================================================= + +export const getTableLogValues = R.compose( + R.prop('tableLogValues'), + getDebuggerState +); + +export const getTableLogSources = R.compose(R.keys, getTableLogValues); + +export const getTableLogsByNodeId = R.curry((nodeId, state) => + R.compose(R.pathOr([], ['tableLogValues', nodeId]), getDebuggerState)(state) +); diff --git a/packages/xod-client/src/editor/actionTypes.js b/packages/xod-client/src/editor/actionTypes.js index 47513d3b..5da06c1b 100644 --- a/packages/xod-client/src/editor/actionTypes.js +++ b/packages/xod-client/src/editor/actionTypes.js @@ -69,3 +69,6 @@ export const NODE_PROPERTY_UPDATING = 'NODE_PROPERTY_UPDATING'; export const SHOW_COLORPICKER_WIDGET = 'SHOW_COLORPICKER_WIDGET'; export const HIDE_COLORPICKER_WIDGET = 'HIDE_COLORPICKER_WIDGET'; + +export const OPEN_TABLE_LOG_TAB = 'OPEN_TABLE_LOG_TAB'; +export const CHANGE_TABLE_LOG_SHEET = 'CHANGE_TABLE_LOG_SHEET'; diff --git a/packages/xod-client/src/editor/actions.js b/packages/xod-client/src/editor/actions.js index 7b541ca1..dd7a3509 100644 --- a/packages/xod-client/src/editor/actions.js +++ b/packages/xod-client/src/editor/actions.js @@ -940,3 +940,39 @@ export const tweakNodeProperty = (nodeId, kind, key, value) => ( }) ); }; + +export const openTableLogTab = nodeId => (dispatch, getState) => { + const sheets = DebuggerSelectors.getTableLogsByNodeId(nodeId, getState()); + return dispatch({ + type: ActionType.OPEN_TABLE_LOG_TAB, + payload: { + nodeId, + activeSheetIndex: sheets.length > 0 ? sheets.length - 1 : 0, + }, + }); +}; + +export const changeActiveSheet = R.curry((tabId, nodeId, newSheetIndex) => ({ + type: ActionType.CHANGE_TABLE_LOG_SHEET, + payload: { + tabId, + nodeId, + newSheetIndex, + }, +})); + +export const changeTableLogSource = (tabId, nodeId) => (dispatch, getState) => { + const newSourceSheets = DebuggerSelectors.getTableLogsByNodeId( + nodeId, + getState() + ); + const newSheetIndex = R.max(0, newSourceSheets.length - 1); + dispatch({ + type: ActionType.CHANGE_TABLE_LOG_SHEET, + payload: { + tabId, + nodeId, + newSheetIndex, + }, + }); +}; diff --git a/packages/xod-client/src/editor/components/TabtestEditor.jsx b/packages/xod-client/src/editor/components/TabtestEditor.jsx index f079ee43..6208fea1 100644 --- a/packages/xod-client/src/editor/components/TabtestEditor.jsx +++ b/packages/xod-client/src/editor/components/TabtestEditor.jsx @@ -99,6 +99,7 @@ const TabtestEditor = ({
, tab => { + // Render component only for PATCH or DEBUGGER + // tab types + if (tab.type !== TAB_TYPES.PATCH && tab.type !== TAB_TYPES.DEBUGGER) + return null; + // Do not render component if opened tab // is in EditingCppImplementation mode. if (tab.editedAttachment) return null; @@ -139,12 +145,37 @@ class Editor extends React.Component { ); } + renderTableLogTab() { + const { currentTab } = this.props; + + return foldMaybe( + null, + tab => { + // Render only for TABLE_LOG tab type + if (tab.type !== TAB_TYPES.TABLE_LOG) return null; + + return ( + + ); + }, + currentTab + ); + } + renderOpenedAttachmentEditorTabs() { return foldMaybe( null, currentTab => { const tabs = this.props.attachmentEditorTabs.map( ({ id, type, patchPath, editedAttachment }) => { + // Render only for PATCH or DEBUGGER tab types + if (type !== TAB_TYPES.PATCH && type !== TAB_TYPES.DEBUGGER) + return null; + const patch = XP.getPatchByPathUnsafe( patchPath, this.props.project @@ -156,11 +187,6 @@ class Editor extends React.Component { R.propOr('', editedAttachment, XP.MANAGED_ATTACHMENT_TEMPLATES) ); - const currentPatchPath = explodeMaybe( - 'No currentPatchPath, but currentTab exists', - this.props.currentPatchPath - ); - switch (editedAttachment) { case XP.TABTEST_MARKER_PATH: return ( @@ -177,11 +203,9 @@ class Editor extends React.Component { } isInDebuggerTab={type === TAB_TYPES.DEBUGGER} isRunning={this.props.isTabtestRunning} - onRunClick={() => - this.props.actions.runTabtest(currentPatchPath) - } + onRunClick={() => this.props.actions.runTabtest(patchPath)} onClose={this.props.actions.closeAttachmentEditor} - patchPath={currentPatchPath} + patchPath={patchPath} /> ); @@ -200,7 +224,7 @@ class Editor extends React.Component { } isInDebuggerTab={type === TAB_TYPES.DEBUGGER} onClose={this.props.actions.closeAttachmentEditor} - patchPath={currentPatchPath} + patchPath={patchPath} /> ); @@ -282,6 +306,7 @@ class Editor extends React.Component { > {this.renderOpenedPatchTab()} {this.renderOpenedAttachmentEditorTabs()} + {this.renderTableLogTab()} diff --git a/packages/xod-client/src/editor/containers/Patch/index.jsx b/packages/xod-client/src/editor/containers/Patch/index.jsx index 802aca5e..56119441 100644 --- a/packages/xod-client/src/editor/containers/Patch/index.jsx +++ b/packages/xod-client/src/editor/containers/Patch/index.jsx @@ -382,6 +382,7 @@ const mapDispatchToProps = dispatch => ({ addInteractiveNode: ProjectActions.addInteractiveNode, focusBoundValue: EditorActions.focusBoundValue, focusLabel: EditorActions.focusLabel, + openTableLogTab: EditorActions.openTableLogTab, }, dispatch ), diff --git a/packages/xod-client/src/editor/containers/Patch/modes/debugging.jsx b/packages/xod-client/src/editor/containers/Patch/modes/debugging.jsx index 164b8e01..137d3a3f 100644 --- a/packages/xod-client/src/editor/containers/Patch/modes/debugging.jsx +++ b/packages/xod-client/src/editor/containers/Patch/modes/debugging.jsx @@ -16,6 +16,8 @@ const debuggingMode = R.merge(selectingMode, { onNodeDoubleClick(api, nodeId, patchPath) { if (R.contains(patchPath, R.keys(XP.MANAGED_ATTACHMENT_FILENAMES))) { api.props.actions.openAttachmentEditor(patchPath); + } else if (XP.isTableLogPatchPath(patchPath)) { + api.props.actions.openTableLogTab(nodeId); } else if ( XP.isConstantNodeType(patchPath) || XP.isTweakPath(patchPath) || diff --git a/packages/xod-client/src/editor/containers/Patch/modes/selecting.jsx b/packages/xod-client/src/editor/containers/Patch/modes/selecting.jsx index 550e5faa..1aadc555 100644 --- a/packages/xod-client/src/editor/containers/Patch/modes/selecting.jsx +++ b/packages/xod-client/src/editor/containers/Patch/modes/selecting.jsx @@ -231,6 +231,8 @@ const selectingMode = { onNodeDoubleClick(api, nodeId, patchPath) { if (R.contains(patchPath, R.keys(XP.MANAGED_ATTACHMENT_FILENAMES))) { api.props.actions.openAttachmentEditor(patchPath); + } else if (XP.isTableLogPatchPath(patchPath)) { + api.props.actions.openTableLogTab(nodeId); } else if ( XP.isConstantNodeType(patchPath) || XP.isTweakPath(patchPath) || diff --git a/packages/xod-client/src/editor/containers/TableLog.jsx b/packages/xod-client/src/editor/containers/TableLog.jsx new file mode 100644 index 00000000..c66904f7 --- /dev/null +++ b/packages/xod-client/src/editor/containers/TableLog.jsx @@ -0,0 +1,229 @@ +import * as R from 'ramda'; +import React from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + +import ReactDataSheet from 'react-datasheet'; +import { Icon } from 'react-fa'; + +import * as Actions from '../actions'; +import * as DebuggerSelectors from '../../debugger/selectors'; + +import { addConfirmation } from '../../messages/actions'; +import { + LOG_COPIED, + LOG_COPY_NOT_SUPPORTED, + logCopyError, + logSaveError, +} from '../../debugger/messages'; + +// :: [[String]] -> [[{ value, readOnly }]] +const tableLogToDataSheet = R.map( + R.map( + R.applySpec({ + value: R.identity, + readOnly: R.T, + }) + ) +); + +const gridToTsv = R.compose(R.join('\n'), R.map(R.join('\t'))); + +class TableLog extends React.Component { + constructor(props) { + super(props); + + this.getSheet = this.getSheet.bind(this); + + this.onPrevSheetClick = this.onPrevSheetClick.bind(this); + this.onNextSheetClick = this.onNextSheetClick.bind(this); + this.onCopyLogClicked = this.onCopyLogClicked.bind(this); + this.onSaveLogClicked = this.onSaveLogClicked.bind(this); + this.onChangeSource = this.onChangeSource.bind(this); + } + + onPrevSheetClick() { + const prevSheetIndex = R.max(0, this.props.sheetIndex - 1); + this.props.actions.changeActiveSheet( + this.props.tabId, // TODO: what's about debugger? + this.props.nodeId, + prevSheetIndex + ); + } + + onNextSheetClick() { + const nextSheetIndex = R.unless(R.equals(this.getSheetsAmount()), R.add(1))( + this.props.sheetIndex + ); + this.props.actions.changeActiveSheet( + this.props.tabId, + this.props.nodeId, + nextSheetIndex + ); + } + + onCopyLogClicked() { + const copy = R.path(['navigator', 'clipboard', 'writeText'], window); + if (!copy) { + this.props.actions.addError(LOG_COPY_NOT_SUPPORTED); + return; + } + + const tsv = gridToTsv(this.getSheet()); + window.navigator.clipboard.writeText(tsv).then( + () => { + this.props.actions.addConfirmation(LOG_COPIED); + }, + err => { + this.props.actions.addError(logCopyError(err)); + } + ); + } + + onSaveLogClicked() { + const tsv = gridToTsv(this.getSheet()); + const defaultFilename = `log-${this.props.nodeId}-${ + this.props.sheetIndex + }.txt`; + + try { + const data = new window.Blob([tsv], { type: 'text/plain' }); + const file = window.URL.createObjectURL(data); + + const link = document.createElement('a'); + link.download = defaultFilename; + link.href = file; + link.click(); + + // We need to manually revoke the object URL to avoid memory leaks. + window.URL.revokeObjectURL(file); + } catch (err) { + this.props.actions.addError(logSaveError(err)); + } + } + + onChangeSource(event) { + const sourceNodeId = event.target.value; + this.props.actions.changeSource(this.props.tabId, sourceNodeId); + } + + getSheet() { + return R.propOr([], this.props.sheetIndex, this.props.document); + } + + getSheetsAmount() { + return this.props.document.length; + } + + isEmptySheet() { + return this.getSheet().length === 0; + } + + render() { + const hasNoSources = this.props.sources.length === 0; + const sheetAmount = + this.getSheetsAmount() === 0 ? 1 : this.getSheetsAmount(); + + return ( +
+
+
+ +
+
+ +
+ {this.props.sheetIndex + 1} / {sheetAmount} +
+ +
+
+ + +
+
+
+ {this.isEmptySheet() ? ( + No data stored + ) : ( + cell.value} + overflow={'nowrap'} + /> + )} +
+
+ ); + } +} + +TableLog.propTypes = { + // from parent + tabId: PropTypes.string.isRequired, + // connect + sources: PropTypes.arrayOf(PropTypes.string).isRequired, + document: PropTypes.array.isRequired, + nodeId: PropTypes.string.isRequired, + sheetIndex: PropTypes.number.isRequired, + actions: PropTypes.objectOf(PropTypes.func), +}; + +const mapStateToProps = (state, { nodeId }) => + R.applySpec({ + sources: DebuggerSelectors.getTableLogSources, + document: DebuggerSelectors.getTableLogsByNodeId(nodeId), + })(state); + +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators( + { + changeActiveSheet: Actions.changeActiveSheet, + changeSource: Actions.changeTableLogSource, + addConfirmation, + }, + dispatch + ), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TableLog); diff --git a/packages/xod-client/src/editor/reducer.js b/packages/xod-client/src/editor/reducer.js index 40c5edb5..e9f6dd31 100644 --- a/packages/xod-client/src/editor/reducer.js +++ b/packages/xod-client/src/editor/reducer.js @@ -11,6 +11,7 @@ import { DEFAULT_PANNING_OFFSET } from '../project/nodeLayout'; import { MAIN_PATCH_PATH } from '../project/constants'; import { DEBUGGER_TAB_ID, + TABLE_LOG_TAB_ID, FOCUS_AREAS, SELECTION_ENTITY_TYPE, TAB_TYPES, @@ -130,34 +131,52 @@ const setPropsToTab = R.curry((id, props, state) => )(state) ); -const addTabWithProps = R.curry((id, type, patchPath, state) => { +const getTabNewIndex = state => { const tabs = R.prop('tabs')(state); const lastIndex = R.reduce( (acc, tab) => R.pipe(R.prop('index'), R.max(acc))(tab), -1, R.values(tabs) ); - const newIndex = R.inc(lastIndex); + return R.inc(lastIndex); +}; - return setPropsToTab( +const addTabWithProps = R.curry((id, type, patchPath, state) => + setPropsToTab( id, { id, patchPath, - index: newIndex, + index: getTabNewIndex(state), type, offset: DEFAULT_PANNING_OFFSET, editedAttachment: null, }, state - ); -}); + ) +); const addPatchTab = R.curry((newId, patchPath, state) => { if (!patchPath) return state; return addTabWithProps(newId, TAB_TYPES.PATCH, patchPath, state); }); +const openTableLogTab = R.curry((nodeId, activeSheetIndex, state) => + setPropsToTab( + TABLE_LOG_TAB_ID, + R.unless( + () => isTabOpened(TABLE_LOG_TAB_ID, state), + R.merge(R.__, { index: getTabNewIndex(state) }) + )({ + id: TABLE_LOG_TAB_ID, + type: TAB_TYPES.TABLE_LOG, + nodeId, + activeSheetIndex, + }), + state + ) +); + const applyTabSort = (tab, payload) => { if (R.not(R.has(tab.id, payload))) { return tab; @@ -711,6 +730,20 @@ const editorReducer = (state = initialState, action) => { R.lensProp('pointingPopups'), R.assoc('colorPickerWidget', null) )(state); + case EAT.OPEN_TABLE_LOG_TAB: + return R.compose( + R.assoc('currentTabId', TABLE_LOG_TAB_ID), + openTableLogTab(action.payload.nodeId, action.payload.activeSheetIndex) + )(state); + case EAT.CHANGE_TABLE_LOG_SHEET: + return setPropsToTab( + action.payload.tabId, + { + nodeId: action.payload.nodeId, + activeSheetIndex: action.payload.newSheetIndex, + }, + state + ); default: return state; diff --git a/packages/xod-client/src/editor/selectors.js b/packages/xod-client/src/editor/selectors.js index 3a6d7cf3..6934981f 100644 --- a/packages/xod-client/src/editor/selectors.js +++ b/packages/xod-client/src/editor/selectors.js @@ -1,7 +1,7 @@ import * as R from 'ramda'; import { Maybe } from 'ramda-fantasy'; import { createSelector } from 'reselect'; -import { mapIndexed, foldMaybe } from 'xod-func-tools'; +import { mapIndexed, foldMaybe, maybeProp } from 'xod-func-tools'; import * as XP from 'xod-project'; import { @@ -32,37 +32,49 @@ export const getCurrentTabId = R.pipe(getEditor, R.prop('currentTabId'), Maybe); export const getCurrentTab = createSelector( [getCurrentTabId, getTabs], - (maybeTabId, tabs) => maybeTabId.chain(R.compose(Maybe, R.prop(R.__, tabs))) + (maybeTabId, tabs) => R.chain(maybeProp(R.__, tabs))(maybeTabId) ); export const getCurrentPatchPath = createSelector( getCurrentTab, - R.map(R.prop('patchPath')) + R.chain(maybeProp('patchPath')) ); export const getCurrentPatchOffset = createSelector( getCurrentTab, - foldMaybe(DEFAULT_PANNING_OFFSET, R.prop('offset')) + foldMaybe(DEFAULT_PANNING_OFFSET, R.propOr(DEFAULT_PANNING_OFFSET, 'offset')) ); +const isTabTypeEq = R.curry((expected, tab) => tab.type === expected); + +const setTabLabel = R.assoc('label'); + // :: State -> EditorTabs export const getPreparedTabs = createSelector( [getCurrentTabId, getTabs], (maybeCurrentTabId, tabs) => { const currentTabId = foldMaybe(null, R.identity, maybeCurrentTabId); - return R.map(tab => { - const patchPath = tab.patchPath; - - const label = - tab.type === TAB_TYPES.DEBUGGER - ? 'Debugger' - : XP.getBaseName(patchPath); - - return R.merge(tab, { - label, - isActive: currentTabId === tab.id, - }); - }, tabs); + return R.map( + R.compose( + tab => R.assoc('isActive', currentTabId === tab.id, tab), + R.cond([ + [isTabTypeEq(TAB_TYPES.TABLE_LOG), setTabLabel('Table Logs')], + [isTabTypeEq(TAB_TYPES.DEBUGGER), setTabLabel('Debugger')], + [ + isTabTypeEq(TAB_TYPES.PATCH), + tab => + R.compose( + setTabLabel(R.__, tab), + XP.getBaseName, + R.prop('patchPath') + )(tab), + ], + // It should not be possible: + [R.T, setTabLabel('Unknown Tab Type')], + ]) + ), + tabs + ); } );