Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to language-switch between TS and Python #747

Merged
merged 1 commit into from Jul 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -20,12 +20,14 @@ interface IPropsFromRedux {
files: IFile[];
activeFile: IFile;
isCustomFunctionSolution: boolean;
isSolutionPython: boolean;
}

const mapStateToProps = (state: IReduxState): IPropsFromRedux => ({
files: selectors.editor.getActiveSolution(state).files,
activeFile: selectors.editor.getActiveFile(state),
isCustomFunctionSolution: selectors.editor.getIsActiveSolutionCF(state),
isSolutionPython: selectors.editor.getIsActiveSolutionPython(state),
});

interface IActionsFromRedux {
Expand All @@ -42,12 +44,17 @@ const FileSwitcherPivot = ({
files,
activeFile,
isCustomFunctionSolution,
isSolutionPython,
openFile,
}: IProps) => (
<PivotBar
items={files
.filter(file => {
if (isCustomFunctionSolution) {
if (isSolutionPython) {
// For Python, only show the script file and nothing else (since don't support HTML/CSS/Libraries)
return file.name === SCRIPT_FILE_NAME;
} else if (isCustomFunctionSolution) {
// Likewise, for Custom Functions, only show the script and the libraries (since UI-less)
return [SCRIPT_FILE_NAME, LIBRARIES_FILE_NAME].includes(file.name);
} else {
return true;
Expand Down
1 change: 0 additions & 1 deletion packages/editor/src/pages/Editor/store/editor/actions.ts
@@ -1,5 +1,4 @@
import { createAction, createAsyncAction } from 'typesafe-actions';
import { History } from 'history';

export const open = createAction('EDITOR_OPEN');
export const hide = createAction('EDITOR_HIDE');
Expand Down
7 changes: 7 additions & 0 deletions packages/editor/src/pages/Editor/store/editor/selectors.ts
Expand Up @@ -7,6 +7,8 @@ import {
} from '../solutions/selectors';

import { NULL_SOLUTION, NULL_FILE } from '../../../../constants';
import { findScript } from 'common/lib/utilities/solution';
import languageMap from '../../../../utils/languageMap';

export const getActiveSolution = (
state: IState,
Expand Down Expand Up @@ -58,6 +60,11 @@ export const getIsActiveSolutionCF = (state: IState): boolean => {
return solution.options.isCustomFunctionsSolution;
};

export const getIsActiveSolutionPython = (state: IState): boolean => {
const solution = getActiveSolution(state);
return findScript(solution).language === languageMap.python;
};

export const getIsActiveSolutionTrusted = createSelector(
[getActiveSolution],
(activeSolution: ISolution) => !activeSolution.options.isUntrusted,
Expand Down
35 changes: 23 additions & 12 deletions packages/editor/src/pages/Editor/store/footer/selectors.ts
Expand Up @@ -9,9 +9,9 @@ import {

// selectors
import { createSelector } from 'reselect';
import { getActiveFile } from '../editor/selectors';
import { getActiveFile, getActiveSolution } from '../editor/selectors';
import { getIsWeb, get as getHost } from '../host/selectors';
import { getMode, IHeaderItem } from '../header/selectors';
import { getMode } from '../header/selectors';
import { getPrettyEditorTheme } from '../settings/selectors';

// actions
Expand All @@ -26,6 +26,9 @@ import {
solutions,
settings,
} from '../actions';
import { getPythonConfigIfAny } from '../../../../utils/python';
import { SCRIPT_FILE_NAME } from 'common/lib/utilities/solution';
import languageMap from '../../../../utils/languageMap';

const actions = {
dialog,
Expand All @@ -39,15 +42,6 @@ const actions = {
settings,
};

const languageMap = {
typescript: 'TypeScript',
javascript: 'JavaScript',
css: 'CSS',
html: 'HTML',
json: 'JSON',
python: 'Python',
};

export const getItems = createSelector(
[getMode, getIsWeb, getHost],
(
Expand Down Expand Up @@ -91,16 +85,33 @@ export const getItems = createSelector(
);

export const getFarItems = createSelector(
[getMode, getActiveFile, getPrettyEditorTheme],
[getMode, getActiveSolution, getActiveFile, getPrettyEditorTheme],
(
mode: 'normal' | 'settings' | 'null-solution',
activeSolution: ISolution,
activeFile: IFile,
currentEditorTheme: string,
) => [
{
hidden: !languageMap[activeFile.language.toLowerCase()],
key: 'editor-language',
text: languageMap[activeFile.language.toLowerCase()],
subMenuProps:
activeFile.name === SCRIPT_FILE_NAME && getPythonConfigIfAny()
? {
isBeakVisible: true,
items: [languageMap.typescript, languageMap.python].map(language => ({
key: language,
text: language,
actionCreator: () =>
actions.solutions.changeLanguage({
solutionId: activeSolution.id,
fileId: activeFile.id,
language: language,
}),
})),
}
: null,
},
{
hidden: mode === 'settings',
Expand Down
12 changes: 12 additions & 0 deletions packages/editor/src/pages/Editor/store/solutions/actions.ts
Expand Up @@ -18,6 +18,18 @@ export const edit = createAction('SOLUTIONS_EDIT', resolve => {
resolve({ id, solution, fileId, file, timestamp: Date.now() });
});

export const changeLanguage = createAction('SOLUTION_CHANGE_LANGUAGE', resolve => {
return ({
solutionId,
fileId,
language,
}: {
solutionId: string;
fileId: string;
language: string;
}) => resolve({ solutionId, fileId, language });
});

export const updateLastOpened = createAction('SOLUTIONS_UPDATE_LAST_OPENED', resolve => {
return ({ solutionId, fileId }) =>
resolve({ solutionId, fileId, timestamp: Date.now() });
Expand Down
14 changes: 13 additions & 1 deletion packages/editor/src/pages/Editor/store/solutions/reducer.ts
Expand Up @@ -107,7 +107,7 @@ const files = (state: IFilesState = {}, action: ISolutionsAction): IFilesState =
...filesById,
};

case getType(solutionActions.edit):
case getType(solutionActions.edit): {
const { file, fileId } = action.payload;
if (!file || !fileId) {
return state;
Expand All @@ -121,6 +121,18 @@ const files = (state: IFilesState = {}, action: ISolutionsAction): IFilesState =
dateLastModified: action.payload.timestamp,
},
};
}

case getType(solutionActions.changeLanguage): {
const { fileId, language } = action.payload;
return {
...state,
[fileId]: {
...state[fileId],
language: language,
},
};
}

case getType(solutionActions.updateLastOpened):
return {
Expand Down
50 changes: 30 additions & 20 deletions packages/editor/src/utils/index.ts
@@ -1,6 +1,11 @@
import createGUID from 'uuid';
import { LIBRARIES_FILE_NAME, SCRIPT_FILE_NAME } from 'common/lib/utilities/solution';
import {
LIBRARIES_FILE_NAME,
SCRIPT_FILE_NAME,
findScript,
} from 'common/lib/utilities/solution';
import { getBoilerplateFiles } from '../newSolutionData';
import languageMap from './languageMap';

export function setUpMomentJsDurationDefaults(momentInstance: {
relativeTimeThreshold(threshold: string, limit: number): boolean;
Expand Down Expand Up @@ -58,25 +63,30 @@ export const convertSnippetToSolution = (snippet: ISnippet): ISolution => {
export const convertSolutionToSnippet = (solution: ISolution): ISnippet => {
const { name, description, host, files } = solution;

const snippetFiles = Object.entries({
script: file => file.name === SCRIPT_FILE_NAME,
template: file => file.name === 'index.html',
style: file => file.name === 'index.css',
libraries: file => file.name === LIBRARIES_FILE_NAME,
})
.map(([fileName, fileSelector]) => [fileName, files.find(fileSelector)])
.filter(([fileName, file]) => file !== undefined)
.reduce((obj, [fileName, file]) => {
const name = fileName as string;
const f = file as IFile;
return {
...obj,
[name]:
f.name === LIBRARIES_FILE_NAME
? f.content
: { content: f.content, language: f.language },
};
}, {});
const mainScriptFile = findScript(solution);

const snippetFiles =
mainScriptFile.language === languageMap.python
? { script: mainScriptFile.content }
: Object.entries({
script: file => file.name === SCRIPT_FILE_NAME,
template: file => file.name === 'index.html',
style: file => file.name === 'index.css',
libraries: file => file.name === LIBRARIES_FILE_NAME,
})
.map(([fileName, fileSelector]) => [fileName, files.find(fileSelector)])
.filter(([fileName, file]) => file !== undefined)
.reduce((obj, [fileName, file]) => {
const name = fileName as string;
const f = file as IFile;
return {
...obj,
[name]:
f.name === LIBRARIES_FILE_NAME
? f.content || ''
: { content: f.content, language: f.language },
};
}, {});

return {
name,
Expand Down
10 changes: 10 additions & 0 deletions packages/editor/src/utils/languageMap.ts
@@ -0,0 +1,10 @@
const languageMap = {
typescript: 'TypeScript',
javascript: 'JavaScript',
css: 'CSS',
html: 'HTML',
json: 'JSON',
python: 'Python',
};

export default languageMap;