// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This module will tie together all of the different calls the gradable module will make.
*
* @module mod_forum/local/grades/local/grader/user_picker
* @copyright 2019 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Templates from 'core/templates';
import Selectors from './user_picker/selectors';
import {get_string as getString} from 'core/str';
const templatePath = 'mod_forum/local/grades/local/grader';
/**
* The Grader User Picker.
*
* @class mod_forum/local/grades/local/grader/user_picker
*/
class UserPicker {
/**
* Constructor for the User Picker.
*
* @constructor mod_forum/local/grades/local/grader/user_picker
* @param {Array} userList List of users
* @param {Function} showUserCallback The callback used to display the user
* @param {Function} preChangeUserCallback The callback to use before changing user
*/
constructor(userList, showUserCallback, preChangeUserCallback) {
this.userList = userList;
this.showUserCallback = showUserCallback;
this.preChangeUserCallback = preChangeUserCallback;
this.currentUserIndex = 0;
// Ensure that render is bound correctly.
this.render = this.render.bind(this);
this.setUserId = this.setUserId.bind(this);
}
/**
* Set the current userid without rendering the change.
* To show the user, call showUser too.
*
* @param {Number} userId
*/
setUserId(userId) {
// Determine the current index based on the user ID.
const userIndex = this.userList.findIndex(user => {
return user.id === parseInt(userId);
});
if (userIndex === -1) {
throw Error(`User with id ${userId} not found`);
}
this.currentUserIndex = userIndex;
}
/**
* Render the user picker.
*/
async render() {
// Create the root node.
this.root = document.createElement('div');
const {html, js} = await this.renderNavigator();
Templates.replaceNodeContents(this.root, html, js);
// Call the showUser function to show the first user immediately.
await this.showUser(this.currentUser);
// Ensure that the event listeners are all bound.
this.registerEventListeners();
}
/**
* Render the navigator itself.
*
* @returns {Promise}
*/
renderNavigator() {
return Templates.renderForPromise(`${templatePath}/user_picker`, {});
}
/**
* Render the current user details for the picker.
*
* @param {Object} context The data used to render the user picker.
* @returns {Promise}
*/
renderUserChange(context) {
return Templates.renderForPromise(`${templatePath}/user_picker/user`, context);
}
/**
* Show the specified user in the picker.
*
* @param {Object} user
*/
async showUser(user) {
const [{html, js}] = await Promise.all([this.renderUserChange(user), this.showUserCallback(user)]);
const userRegion = this.root.querySelector(Selectors.regions.userRegion);
Templates.replaceNodeContents(userRegion, html, js);
// Update the hidden now-grading region so screen readers can announce the user that's currently being graded.
const currentUserRegion = this.root.querySelector(Selectors.regions.currentUser);
currentUserRegion.textContent = await getString('nowgradinguser', 'mod_forum', user.fullname);
}
/**
* Register the event listeners for the user picker.
*/
registerEventListeners() {
this.root.addEventListener('click', async(e) => {
const button = e.target.closest(Selectors.actions.changeUser);
if (button) {
const result = await this.preChangeUserCallback(this.currentUser);
if (!result.failed) {
this.updateIndex(parseInt(button.dataset.direction));
await this.showUser(this.currentUser);
}
}
});
}
/**
* Update the current user index.
*
* @param {Number} direction
* @returns {Number}}
*/
updateIndex(direction) {
this.currentUserIndex += direction;
// Loop around the edges.
if (this.currentUserIndex < 0) {
this.currentUserIndex = this.userList.length - 1;
} else if (this.currentUserIndex > this.userList.length - 1) {
this.currentUserIndex = 0;
}
return this.currentUserIndex;
}
/**
* Get the details of the user currently shown with the total number of users, and the 1-indexed count of the
* current user.
*
* @returns {Object}
*/
get currentUser() {
return {
...this.userList[this.currentUserIndex],
total: this.userList.length,
displayIndex: this.currentUserIndex + 1,
};
}
/**
* Get the root node for the User Picker.
*
* @returns {HTMLElement}
*/
get rootNode() {
return this.root;
}
}
/**
* Create a new user picker.
*
* @param {Array} users The list of users
* @param {Function} showUserCallback The function to call to show a specific user
* @param {Function} preChangeUserCallback The fucntion to call to save the grade for the current user
* @param {Number} [currentUserID] The userid of the current user
* @returns {UserPicker}
*/
export default async(
users,
showUserCallback,
preChangeUserCallback,
{
initialUserId = null,
} = {}
) => {
const userPicker = new UserPicker(users, showUserCallback, preChangeUserCallback);
if (initialUserId) {
userPicker.setUserId(initialUserId);
}
await userPicker.render();
return userPicker;
};