diff --git a/src/helpers/groups.js b/src/helpers/groups.js index b7668bcc1..555ebac9d 100644 --- a/src/helpers/groups.js +++ b/src/helpers/groups.js @@ -1,4 +1,8 @@ -export const isExam = ({ privateData: { examBegin, examEnd } }) => { +import { safeGet } from './common'; + +export const isExam = group => { + const examBegin = safeGet(group, ['privateData', 'examBegin']); + const examEnd = safeGet(group, ['privateData', 'examEnd']); const now = Date.now() / 1000; return examBegin && examEnd && examEnd > now && examBegin <= now; }; diff --git a/src/pages/GroupExams/GroupExams.js b/src/pages/GroupExams/GroupExams.js index 7fc7ac7a1..554fd0169 100644 --- a/src/pages/GroupExams/GroupExams.js +++ b/src/pages/GroupExams/GroupExams.js @@ -25,13 +25,19 @@ import { groupExamLocksSelector } from '../../redux/selectors/groupExamLocks'; import { lockedStudentsOfGroupSelector } from '../../redux/selectors/usersGroups'; import { loggedInUserIdSelector } from '../../redux/selectors/auth'; import { isLoggedAsSuperAdmin, loggedInUserSelector } from '../../redux/selectors/users'; +import { getJsData } from '../../redux/helpers/resourceManager'; import withLinks from '../../helpers/withLinks'; import { hasPermissions, safeGet } from '../../helpers/common'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; import { isExam } from '../../helpers/groups'; +const REFRESH_INTERVAL = 1; // [s] + class GroupExams extends Component { + state = { examInProgress: false }; + intervalHandler = null; + static loadAsync = ({ groupId }, dispatch) => Promise.all([ dispatch(fetchGroupIfNeeded(groupId)).then(({ value: group }) => @@ -41,11 +47,31 @@ class GroupExams extends Component { ), ]); + static getDerivedStateFromProps({ group }) { + const groupJs = getJsData(group); + const examInProgress = Boolean(groupJs && isExam(groupJs)); + return { examInProgress }; + } + + periodicRefresh = () => { + const newState = GroupExams.getDerivedStateFromProps(this.props, this.state); + if (newState.examInProgress !== this.state.examInProgress) { + this.setState(newState); + } + }; + componentDidMount() { this.props.loadAsync(); if (this.props.params.examId) { this.props.loadGroupExamLocks(); } + + if (window && 'setInterval' in window) { + if (this.intervalHandler) { + window.clearInterval(this.intervalHandler); + } + this.intervalHandler = window.setInterval(this.periodicRefresh, REFRESH_INTERVAL * 1000); + } } componentDidUpdate(prevProps) { @@ -57,6 +83,13 @@ class GroupExams extends Component { } } + componentWillUnmount() { + if (this.intervalHandler) { + window.clearInterval(this.intervalHandler); + this.intervalHandler = null; + } + } + linkFactory = id => { const { params: { groupId, examId = null }, @@ -114,19 +147,19 @@ class GroupExams extends Component { unlimitedHeight> - {(examId || isExam(group)) && hasPermissions(group, 'viewStudents', 'setExamPeriod') && ( + {(examId || this.state.examInProgress) && hasPermissions(group, 'viewStudents', 'setExamPeriod') && ( - {isExam(group) ? ( + {this.state.examInProgress ? ( }> <> {isLoggedIn ? ( - - - - - - - + userError ? ( + + + +

{getErrorMessage(formatMessage)(userError)}

+ +
+ +
+ ) : ( + + + + + + + + ) ) : ( <> {redirect && ( @@ -171,13 +193,14 @@ class Login extends Component { } Login.propTypes = { - login: PropTypes.func.isRequired, params: PropTypes.shape({ redirect: PropTypes.string, }), isLoggedIn: PropTypes.bool.isRequired, instanceId: PropTypes.string, loggedInUser: ImmutablePropTypes.map, + login: PropTypes.func.isRequired, + logout: PropTypes.func.isRequired, reset: PropTypes.func.isRequired, links: PropTypes.object.isRequired, intl: PropTypes.object, @@ -193,6 +216,7 @@ export default withLinks( }), dispatch => ({ login: ({ email, password }) => dispatch(login(email, password)), + logout: () => dispatch(logout()), reset: () => { dispatch(reset('login')); },