Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…tend into P2Fancy
  • Loading branch information
milidieh committed Jun 14, 2021
2 parents 88dc68f + 872bed5 commit 556e14d
Show file tree
Hide file tree
Showing 99 changed files with 8,573 additions and 416 deletions.
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,7 @@ REACT_APP_GITHUB_OAUTH_PROXY_URL=

# Keystroke logging
REACT_APP_CADET_LOGGER=
REACT_APP_CADET_LOGGER_INTERVAL=10000
REACT_APP_CADET_LOGGER_INTERVAL=10000

# Link to interative-sicp
REACT_APP_INTERACTIVE_SICP_URL="http://127.0.0.1:8080/"
2 changes: 2 additions & 0 deletions .github/workflows/build-development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
ln -s index.html build/playground.html
ln -s index.html build/contributors.html
ln -s index.html build/sourcecast.html
ln -s index.html build/interactive-sicp.html
# the `ln`s above are a hack to make GitHub Pages route /playground
# and /contributors etc to index, instead of 404-ing.
env:
Expand All @@ -50,6 +51,7 @@ jobs:
REACT_APP_GOOGLE_API_KEY: ${{ secrets.REACT_APP_GOOGLE_API_KEY }}
REACT_APP_GOOGLE_APP_ID: ${{ secrets.REACT_APP_GOOGLE_APP_ID }}
REACT_APP_PLAYGROUND_ONLY: "TRUE"
REACT_APP_ENABLE_GITHUB_ASSESSMENTS: "TRUE"
REACT_APP_VERSION: ${{ format('{0}-{1}', github.sha, steps.get-time.outputs.time) }}
REACT_APP_ENVIRONMENT: "pages"
REACT_APP_MODULE_BACKEND_URL: https://source-academy.github.io/modules
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ terraform*
.env.test.local
.env.production.local
.idea/
.vscode/
cadet-frontend.iml

npm-debug.log*
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ The frontend can be configured to disable itself (based on user's system time) d
1. `REACT_APP_PLAYGROUND_ONLY`: Whether to build the "playground-only" version, which disables the Academy components, so only the Playground is available. This is what we deploy onto [GitHub Pages](https://source-academy.github.io).
1. `REACT_APP_ENABLE_GAME`: Whether to enable the game. Off by default.
1. `REACT_APP_ENABLE_ACHIEVEMENTS`: Whether to enable the incentives/achievements system. Off by default.
1. `REACT_APP_ENABLE_GITHUB_ASSESSMENTS`: Whether to enable the GitHub Assessments feature. Off by default.

## Development

Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,19 @@
"@sourceacademy/sharedb-ace": "^2.0.2",
"@sourceacademy/sling-client": "^0.0.1",
"@testing-library/user-event": "^13.1.9",
"@types/react-syntax-highlighter": "^13.5.0",
"@types/uuid": "^8.3.0",
"ace-builds": "^1.4.12",
"acorn": "^8.3.0",
"ag-grid-community": "^25.3.0",
"ag-grid-react": "^25.3.0",
"array-move": "^3.0.1",
"better-react-mathjax": "^1.0.2",
"classnames": "^2.3.1",
"connected-react-router": "^6.9.1",
"flexboxgrid": "^6.3.1",
"flexboxgrid-helpers": "^1.1.3",
"js-slang": "^0.5.1",
"js-slang": "^0.5.3",
"konva": "^7.2.5",
"lodash": "^4.17.21",
"lz-string": "^1.4.4",
Expand All @@ -72,12 +74,13 @@
"react-router-dom": "^5.2.0",
"react-simple-keyboard": "^3.1.42",
"react-sortable-hoc": "^1.11.0",
"react-syntax-highlighter": "^15.4.3",
"react-textarea-autosize": "^8.3.3",
"redux": "^4.1.0",
"redux-mock-store": "^1.5.4",
"redux-saga": "^1.1.3",
"showdown": "^1.9.1",
"sourceror": "^0.8.2",
"sourceror": "^0.8.3",
"typesafe-actions": "^5.1.0",
"uuid": "^8.3.2",
"xml2js": "^0.4.23",
Expand Down
4 changes: 4 additions & 0 deletions src/commons/application/Application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Login from '../../pages/login/LoginContainer';
import MissionControlContainer from '../../pages/missionControl/MissionControlContainer';
import NotFound from '../../pages/notFound/NotFound';
import Playground from '../../pages/playground/PlaygroundContainer';
import Sicp from '../../pages/sicp/Sicp';
import SourcecastContainer from '../../pages/sourcecast/SourcecastContainer';
import NavigationBar from '../navigationBar/NavigationBar';
import Constants from '../utils/Constants';
Expand Down Expand Up @@ -164,6 +165,8 @@ const Application: React.FC<ApplicationProps> = props => {
/>
)}
<Route path="/callback/github" component={GitHubCallback} />
<Route exact path="/interactive-sicp" render={redirectToSicp} />
<Route path="/interactive-sicp/:section" component={Sicp} />
{fullPaths}
<Route
exact={true}
Expand All @@ -181,6 +184,7 @@ const Application: React.FC<ApplicationProps> = props => {
const redirectToPlayground = () => <Redirect to="/playground" />;
const redirectToAcademy = () => <Redirect to="/academy" />;
const redirectToLogin = () => <Redirect to="/login" />;
const redirectToSicp = () => <Redirect to="/interactive-sicp/index" />;

/**
* A user routes to /academy,
Expand Down
5 changes: 4 additions & 1 deletion src/commons/application/ApplicationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ export const defaultAchievement: AchievementState = {
};

export const defaultPlayground: PlaygroundState = {
usingSubst: false,
githubSaveInfo: { repoName: '', filePath: '' }
};

Expand Down Expand Up @@ -276,6 +275,10 @@ export const defaultWorkspaceManager: WorkspaceManagerState = {
timeElapsedBeforePause: 0,
timeResumed: 0
},
sicp: {
...createDefaultWorkspace('sicp'),
usingSubst: false
},
githubAssessment: {
...createDefaultWorkspace('githubAssessment'),
hasUnsavedChanges: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ exports[`Application renders correctly 1`] = `
<Route path=\\"/githubassessments/editor\\" component={[Function: C] { displayName: 'withRouter(Connect(GitHubAssessmentWorkspace))', WrappedComponent: [Function: GitHubAssessmentWorkspace], propTypes: { wrappedComponentRef: [Function: bound checkType] { isRequired: [Function: bound checkType] } } }} />
<Route path=\\"/githubassessments/:selectedType?\\" component={[Function: component]} />
<Route path=\\"/callback/github\\" component={[Function: GitHubCallback]} />
<Route exact={true} path=\\"/interactive-sicp\\" render={[Function: redirectToSicp]} />
<Route path=\\"/interactive-sicp/:section\\" component={[Function: Sicp]} />
<Route path=\\"/academy\\" render={[Function: redirectToLogin]} />
<Route path=\\"/mission-control/:assessmentId(-?\\\\\\\\d+)?/:questionId(\\\\\\\\d+)?\\" render={[Function: toIncubator]} />
<Route path=\\"/login\\" render={[Function (anonymous)]} />
Expand Down
2 changes: 1 addition & 1 deletion src/commons/application/actions/InterpreterActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
HANDLE_CONSOLE_LOG
} from '../types/InterpreterTypes';

export const handleConsoleLog = (logString: string, workspaceLocation: WorkspaceLocation) =>
export const handleConsoleLog = (workspaceLocation: WorkspaceLocation, ...logString: string[]) =>
action(HANDLE_CONSOLE_LOG, { logString, workspaceLocation });

export const evalInterpreterSuccess = (value: Value, workspaceLocation: WorkspaceLocation) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ const playgroundWorkspace: WorkspaceLocation = 'playground';

test('handleConsoleLog generates correct action object', () => {
const logString = 'test-log-string';
const action = handleConsoleLog(logString, assessmentWorkspace);
const action = handleConsoleLog(assessmentWorkspace, logString);
expect(action).toEqual({
type: HANDLE_CONSOLE_LOG,
payload: {
logString,
logString: [logString],
workspaceLocation: assessmentWorkspace
}
});
Expand Down
14 changes: 14 additions & 0 deletions src/commons/controlBar/ControlBarCloseButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IconNames } from '@blueprintjs/icons';

import controlButton from '../ControlButton';

type ControlBarCloseButtonProps = OwnProps;

type OwnProps = {
key: string;
handleClose: () => void;
};

export function ControlBarCloseButton(props: ControlBarCloseButtonProps) {
return controlButton('Close', IconNames.CROSS, props.handleClose);
}
15 changes: 15 additions & 0 deletions src/commons/controlBar/ControlBarShowDependenciesButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IconNames } from '@blueprintjs/icons';

import controlButton from '../ControlButton';

type ControlBarShowDependenciesButtonProps = OwnProps;

type OwnProps = {
key: string;
buttonText: string;
handleShowDependencies: () => void;
};

export function ControlBarShowDependenciesButton(props: ControlBarShowDependenciesButtonProps) {
return controlButton(props.buttonText, IconNames.CODE, props.handleShowDependencies);
}
22 changes: 22 additions & 0 deletions src/commons/controlBar/github/ControlBarDisplayMCQButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { IconNames } from '@blueprintjs/icons';

import controlButton from '../../ControlButton';

type ControlBarDisplayMCQButtonProps = DispatchProps & StateProps;

type DispatchProps = {
displayMCQInEditor: () => void;
displayTextInEditor: () => void;
};

type StateProps = {
mcqDisplayed: boolean;
key: string;
};

export const ControlBarDisplayMCQButton: React.FC<ControlBarDisplayMCQButtonProps> = props => {
const label = props.mcqDisplayed ? 'Show MCQ Text' : 'Hide MCQ Text';
const behaviour = props.mcqDisplayed ? props.displayTextInEditor : props.displayMCQInEditor;

return controlButton(label, IconNames.REFRESH, behaviour);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { Octokit } from '@octokit/rest';
import * as React from 'react';
import { useMediaQuery } from 'react-responsive';

import { GitHubState } from '../../features/github/GitHubTypes';
import controlButton from '../ControlButton';
import Constants from '../utils/Constants';
import { GitHubState } from '../../../features/github/GitHubTypes';
import controlButton from '../../ControlButton';
import Constants from '../../utils/Constants';

export type ControlBarGitHubButtonsProps = {
loggedInAs: Octokit | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import * as React from 'react';
import { useSelector } from 'react-redux';
import { useMediaQuery } from 'react-responsive';

import { OverallState } from '../application/ApplicationTypes';
import controlButton from '../ControlButton';
import Constants from '../utils/Constants';
import { OverallState } from '../../application/ApplicationTypes';
import controlButton from '../../ControlButton';
import Constants from '../../utils/Constants';

export type ControlBarGitHubLoginButtonProps = {
onClickLogIn: () => void;
Expand Down
26 changes: 26 additions & 0 deletions src/commons/controlBar/github/ControlBarTaskAddButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { IconNames } from '@blueprintjs/icons';

import controlButton from '../../ControlButton';
import { maximumTasksPerMission } from '../../githubAssessments/GitHubMissionDataUtils';
import { showWarningMessage } from '../../utils/NotificationsHelper';

export type ControlBarTaskAddButtonProps = {
addNewQuestion: () => void;
numberOfTasks: number;
key: string;
};

export const ControlBarTaskAddButton: React.FC<ControlBarTaskAddButtonProps> = props => {
function onClickAdd() {
if (props.numberOfTasks === maximumTasksPerMission) {
showWarningMessage(
'Cannot have more than ' + maximumTasksPerMission + ' Tasks in a Mission!'
);
return;
}

props.addNewQuestion();
}

return controlButton('Add Task', IconNames.ADD, onClickAdd);
};
39 changes: 39 additions & 0 deletions src/commons/controlBar/github/ControlBarTaskDeleteButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { IconNames } from '@blueprintjs/icons';

import controlButton from '../../ControlButton';
import { showSimpleConfirmDialog } from '../../utils/DialogHelper';
import { showWarningMessage } from '../../utils/NotificationsHelper';

export type ControlBarTaskDeleteButtonProps = {
deleteCurrentQuestion: () => void;
numberOfTasks: number;
key: string;
};

export const ControlBarTaskDeleteButton: React.FC<ControlBarTaskDeleteButtonProps> = props => {
async function onClickDelete() {
if (props.numberOfTasks <= 1) {
showWarningMessage('Cannot delete the only remaining task!');
return;
}

const confirmDelete = await showSimpleConfirmDialog({
contents: (
<div>
<p>Warning: you are about to delete a task.</p>
<p>This action cannot be undone.</p>
<p>Please click 'Confirm' to continue, or 'Cancel' to go back.</p>
</div>
),
negativeLabel: 'Cancel',
positiveIntent: 'primary',
positiveLabel: 'Confirm'
});

if (confirmDelete) {
props.deleteCurrentQuestion();
}
}

return controlButton('Delete Current Task', IconNames.DELETE, onClickDelete);
};
76 changes: 76 additions & 0 deletions src/commons/githubAssessments/GitHubMissionCreateDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { AnchorButton, Button, Classes, Dialog, InputGroup, Intent } from '@blueprintjs/core';
import classNames from 'classnames';
import React, { useState } from 'react';

import { showWarningMessage } from '../utils/NotificationsHelper';

export type GitHubMissionCreateDialogResolution = {
confirmSave: boolean;
repoName: string;
};

export type GitHubMissionCreateDialogProps = {
filesToCreate: string[];
userLogin: string;
resolveDialog: (arg: GitHubMissionCreateDialogResolution) => void;
};

export const GitHubMissionCreateDialog: React.FC<GitHubMissionCreateDialogProps> = props => {
const [repositoryName, setrepositoryName] = useState('sa-new-mission-repository');

return (
<Dialog className="missionBrowser" isOpen={true}>
<div className={classNames('githubDialogHeader', Classes.DIALOG_HEADER)}>
<h3>Please confirm your save</h3>
</div>
<div className={classNames('githubDialogBody', Classes.DIALOG_BODY)}>
<div>
<h4>This will create a repository owned by {props.userLogin} with the title:</h4>
<InputGroup
onChange={handleTitleChange}
placeholder={'Enter Repository Title'}
value={repositoryName}
/>
</div>
<div>
<h4>This repository will be created with following files:</h4>
{props.filesToCreate.map(filepath => (
<li key={filepath}>{filepath}</li>
))}
</div>
</div>

<div className={classNames(Classes.DIALOG_FOOTER)}>
<div className={classNames(Classes.DIALOG_FOOTER_ACTIONS)}>
<Button onClick={handleClose}>Close</Button>
<AnchorButton onClick={handleConfirm} intent={Intent.PRIMARY}>
Confirm
</AnchorButton>
</div>
</div>
</Dialog>
);

function handleTitleChange(event: any) {
setrepositoryName(event.target.value);
}

function handleClose() {
props.resolveDialog({
confirmSave: false,
repoName: ''
});
}

function handleConfirm() {
if (repositoryName === '') {
showWarningMessage('Cannot create repository without title!', 2000);
return;
}

props.resolveDialog({
confirmSave: true,
repoName: repositoryName
});
}
};
Loading

0 comments on commit 556e14d

Please sign in to comment.