@@ -359,6 +370,7 @@ export function SubmissionDetails({
return (
{renderGeneralInfo()}
+ {renderLoader()}
{renderDetails()}
{renderSourceFiles()}
diff --git a/judgels-client/src/components/SubmissionDetails/Programming/SubmissionDetails.scss b/judgels-client/src/components/SubmissionDetails/Programming/SubmissionDetails.scss
index bdb446728..b5da5b6fb 100644
--- a/judgels-client/src/components/SubmissionDetails/Programming/SubmissionDetails.scss
+++ b/judgels-client/src/components/SubmissionDetails/Programming/SubmissionDetails.scss
@@ -58,6 +58,10 @@
float: left;
}
+ .pending-loader {
+ margin-bottom: 15px;
+ }
+
.submission-details-image {
overflow: auto;
}
diff --git a/judgels-client/src/modules/jerahmeel/jerahmeelReducer.js b/judgels-client/src/modules/jerahmeel/jerahmeelReducer.js
index 56ab472bf..92a64205f 100644
--- a/judgels-client/src/modules/jerahmeel/jerahmeelReducer.js
+++ b/judgels-client/src/modules/jerahmeel/jerahmeelReducer.js
@@ -5,6 +5,7 @@ import storage from 'redux-persist/es/storage';
import courseReducer from '../../routes/courses/courses/modules/courseReducer';
import courseChapterReducer from '../../routes/courses/courses/single/chapters/modules/courseChapterReducer';
import courseChaptersReducer from '../../routes/courses/courses/single/chapters/modules/courseChaptersReducer';
+import chapterProblemReducer from '../../routes/courses/courses/single/chapters/single/problems/single/modules/chapterProblemReducer';
import problemSetReducer from '../../routes/problems/problemsets/modules/problemSetReducer';
import problemSetProblemReducer from '../../routes/problems/problemsets/single/problems/modules/problemSetProblemReducer';
@@ -12,6 +13,7 @@ export default combineReducers({
course: persistReducer({ key: 'jerahmeelCourse', storage }, courseReducer),
courseChapter: persistReducer({ key: 'jerahmeelCourseChapter', storage }, courseChapterReducer),
courseChapters: courseChaptersReducer,
+ chapterProblem: chapterProblemReducer,
problemSet: persistReducer({ key: 'jerahmeelProblemSet', storage }, problemSetReducer),
problemSetProblem: persistReducer({ key: 'jerahmeelProblemSetProblem', storage }, problemSetProblemReducer),
});
diff --git a/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/ChapterProblemPage/ChapterProblemPage.jsx b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/ChapterProblemPage/ChapterProblemPage.jsx
index 668ee23f4..245c3f4b4 100644
--- a/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/ChapterProblemPage/ChapterProblemPage.jsx
+++ b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/ChapterProblemPage/ChapterProblemPage.jsx
@@ -14,6 +14,7 @@ import { ProblemType } from '../../../../../../../../../modules/api/sandalphon/p
import { selectCourse } from '../../../../../../modules/courseSelectors';
import { selectCourseChapter } from '../../../../modules/courseChapterSelectors';
import { selectCourseChapters } from '../../../../modules/courseChaptersSelectors';
+import { selectChapterProblemKey } from '../modules/chapterProblemSelectors';
import { selectStatementLanguage } from '../../../../../../../../../modules/webPrefs/webPrefsSelectors';
import * as chapterProblemActions from '../../modules/chapterProblemActions';
import * as breadcrumbsActions from '../../../../../../../../../modules/breadcrumbs/breadcrumbsActions';
@@ -25,33 +26,17 @@ export class ChapterProblemPage extends Component {
response: undefined,
};
- async componentDidMount() {
- const response = await this.props.onGetProblemWorksheet(
- this.props.chapter.jid,
- this.props.match.params.problemAlias,
- this.props.statementLanguage
- );
-
- this.setState({
- response,
- });
-
- this.props.onPushBreadcrumb(this.props.match.url, response.problem.alias);
-
- sendGAEvent({ category: 'Courses', action: 'View course problem', label: this.props.course.name });
- sendGAEvent({ category: 'Courses', action: 'View chapter problem', label: this.props.chapter.name });
- sendGAEvent({
- category: 'Courses',
- action: 'View problem',
- label: this.props.chapterName + ': ' + this.props.match.params.problemAlias,
- });
+ componentDidMount() {
+ this.refreshProblem();
}
async componentDidUpdate(prevProps) {
- if (this.props.statementLanguage !== prevProps.statementLanguage) {
- await this.componentDidMount();
- } else if (this.props.match.params.problemAlias !== prevProps.match.params.problemAlias) {
- await this.componentDidMount();
+ if (
+ this.props.statementLanguage !== prevProps.statementLanguage ||
+ this.props.chapterProblemKey !== prevProps.chapterProblemKey ||
+ this.props.match.params.problemAlias !== prevProps.match.params.problemAlias
+ ) {
+ await this.refreshProblem();
}
}
@@ -69,6 +54,28 @@ export class ChapterProblemPage extends Component {
);
}
+ refreshProblem = async () => {
+ const response = await this.props.onGetProblemWorksheet(
+ this.props.chapter.jid,
+ this.props.match.params.problemAlias,
+ this.props.statementLanguage
+ );
+
+ this.setState({
+ response,
+ });
+
+ this.props.onPushBreadcrumb(this.props.match.url, response.problem.alias);
+
+ sendGAEvent({ category: 'Courses', action: 'View course problem', label: this.props.course.name });
+ sendGAEvent({ category: 'Courses', action: 'View chapter problem', label: this.props.chapter.name });
+ sendGAEvent({
+ category: 'Courses',
+ action: 'View problem',
+ label: this.props.chapterName + ': ' + this.props.match.params.problemAlias,
+ });
+ };
+
renderHeader = () => {
const { course, chapter, match } = this.props;
@@ -148,6 +155,7 @@ const mapStateToProps = state => ({
course: selectCourse(state),
chapter: selectCourseChapter(state),
chapters: selectCourseChapters(state),
+ chapterProblemKey: selectChapterProblemKey(state),
statementLanguage: selectStatementLanguage(state),
});
const mapDispatchToProps = {
diff --git a/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/Programming/submissions/modules/chapterProblemSubmissionActions.js b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/Programming/submissions/modules/chapterProblemSubmissionActions.js
index 9a281d540..c0bfde420 100644
--- a/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/Programming/submissions/modules/chapterProblemSubmissionActions.js
+++ b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/Programming/submissions/modules/chapterProblemSubmissionActions.js
@@ -34,12 +34,20 @@ export function createSubmission(courseSlug, chapterJid, chapterAlias, problemJi
sourceFiles['sourceFiles.' + key] = data.sourceFiles[key];
});
- await submissionProgrammingAPI.createSubmission(token, chapterJid, problemJid, data.gradingLanguage, sourceFiles);
+ const submission = await submissionProgrammingAPI.createSubmission(
+ token,
+ chapterJid,
+ problemJid,
+ data.gradingLanguage,
+ sourceFiles
+ );
toastActions.showSuccessToast('Solution submitted.');
window.scrollTo(0, 0);
- dispatch(push(`/courses/${courseSlug}/chapters/${chapterAlias}/problems/${problemAlias}/submissions`));
+ dispatch(
+ push(`/courses/${courseSlug}/chapters/${chapterAlias}/problems/${problemAlias}/submissions/${submission.id}`)
+ );
};
}
diff --git a/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/Programming/submissions/single/ChapterProblemSubmissionPage/ChapterProblemSubmissionPage.jsx b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/Programming/submissions/single/ChapterProblemSubmissionPage/ChapterProblemSubmissionPage.jsx
index 4a86382ec..daf9e60f7 100644
--- a/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/Programming/submissions/single/ChapterProblemSubmissionPage/ChapterProblemSubmissionPage.jsx
+++ b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/Programming/submissions/single/ChapterProblemSubmissionPage/ChapterProblemSubmissionPage.jsx
@@ -10,6 +10,8 @@ import { SubmissionDetails } from '../../../../../../../../../../../../component
import { selectStatementLanguage } from '../../../../../../../../../../../../modules/webPrefs/webPrefsSelectors';
import { selectCourse } from '../../../../../../../../../modules/courseSelectors';
import { selectCourseChapter } from '../../../../../../../modules/courseChapterSelectors';
+import { RefreshChapterProblem } from '../../../../modules/chapterProblemReducer';
+import { VerdictCode } from '../../../../../../../../../../../../modules/api/gabriel/verdict';
import * as breadcrumbsActions from '../../../../../../../../../../../../modules/breadcrumbs/breadcrumbsActions';
import * as chapterProblemSubmissionActions from '../../modules/chapterProblemSubmissionActions';
@@ -22,20 +24,10 @@ export class ChapterProblemSubmissionPage extends Component {
containerName: undefined,
};
- async componentDidMount() {
- const { data, profile, problemName, containerName } = await this.props.onGetSubmissionWithSource(
- +this.props.match.params.submissionId,
- this.props.statementLanguage
- );
- const sourceImageUrl = data.source ? undefined : await this.props.onGetSubmissionSourceImage(data.submission.jid);
- this.props.onPushBreadcrumb(this.props.match.url, '#' + data.submission.id);
- this.setState({
- submissionWithSource: data,
- sourceImageUrl,
- profile,
- problemName,
- containerName,
- });
+ currentTimeout;
+
+ componentDidMount() {
+ this.refreshSubmission();
}
async componentWillUnmount() {
@@ -63,6 +55,36 @@ export class ChapterProblemSubmissionPage extends Component {
);
}
+ refreshSubmission = async () => {
+ const { data, profile, problemName, containerName } = await this.props.onGetSubmissionWithSource(
+ +this.props.match.params.submissionId,
+ this.props.statementLanguage
+ );
+ const sourceImageUrl = data.source ? undefined : await this.props.onGetSubmissionSourceImage(data.submission.jid);
+ this.props.onPushBreadcrumb(this.props.match.url, '#' + data.submission.id);
+ this.setState({
+ submissionWithSource: data,
+ sourceImageUrl,
+ profile,
+ problemName,
+ containerName,
+ });
+
+ if (sourceImageUrl) {
+ return;
+ }
+
+ const verdictCode = data.submission.latestGrading && data.submission.latestGrading.verdict.code;
+ if (verdictCode === VerdictCode.PND) {
+ this.currentTimeout = setTimeout(this.refreshSubmission, 2000);
+ } else if (verdictCode === VerdictCode.AC) {
+ if (this.currentTimeout) {
+ clearTimeout(this.currentTimeout);
+ this.props.onRefreshChapterProblem(new Date());
+ }
+ }
+ };
+
renderSubmission = () => {
const { submissionWithSource, profile, sourceImageUrl } = this.state;
const { course, chapter } = this.props;
@@ -79,6 +101,8 @@ export class ChapterProblemSubmissionPage extends Component {
sourceImageUrl={sourceImageUrl}
profile={profile}
problemUrl={`/courses/${course.slug}/chapters/${chapter.alias}/problems/${problemAlias}`}
+ hideSourceFilename
+ showLoaderWhenPending
/>
);
};
@@ -91,6 +115,7 @@ const mapStateToProps = state => ({
});
const mapDispatchToProps = {
+ onRefreshChapterProblem: RefreshChapterProblem,
onGetSubmissionWithSource: chapterProblemSubmissionActions.getSubmissionWithSource,
onGetSubmissionSourceImage: chapterProblemSubmissionActions.getSubmissionSourceImage,
onPushBreadcrumb: breadcrumbsActions.pushBreadcrumb,
diff --git a/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/modules/chapterProblemReducer.js b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/modules/chapterProblemReducer.js
new file mode 100644
index 000000000..5f4ae30c2
--- /dev/null
+++ b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/modules/chapterProblemReducer.js
@@ -0,0 +1,19 @@
+export const initialState = {
+ value: undefined,
+};
+
+export function RefreshChapterProblem(key) {
+ return {
+ type: 'jerahmeel/chapterProblem/PUT',
+ payload: key,
+ };
+}
+
+export default function chapterProblemReducer(state = initialState, action) {
+ switch (action.type) {
+ case 'jerahmeel/chapterProblem/PUT':
+ return { ...state, value: action.payload };
+ default:
+ return state;
+ }
+}
diff --git a/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/modules/chapterProblemSelectors.js b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/modules/chapterProblemSelectors.js
new file mode 100644
index 000000000..2c1a6ee7f
--- /dev/null
+++ b/judgels-client/src/routes/courses/courses/single/chapters/single/problems/single/modules/chapterProblemSelectors.js
@@ -0,0 +1,3 @@
+export function selectChapterProblemKey(state) {
+ return state.jerahmeel.chapterProblem.value;
+}