42_ft_transcendence/frontend/static/js/views/SettingsView.js

333 lines
12 KiB
JavaScript

import {client, lang, navigateTo} from '../index.js';
import {clearElements, fill_errors} from '../utils/formUtils.js'
import AbstractAuthenticatedView from './abstracts/AbstractAuthenticatedView.js';
export default class extends AbstractAuthenticatedView
{
constructor(params)
{
super(params, 'Settings');
this.PROFILE_PICTURE_MAX_SIZE = 2 * 1024 * 1024; // 2MB
}
async postInit()
{
this.avatarInit();
this.usernameInit();
this.passwordInit();
this.deleteInit();
}
deleteInit() {
const deleteInput = document.getElementById('deleteInput');
const deleteModal = document.getElementById('deleteModal');
deleteModal.addEventListener('shown.bs.modal', _ => {
deleteInput.focus();
});
deleteModal.addEventListener('hidden.bs.modal', _ => {
deleteInput.value = '';
});
deleteInput.onkeydown = e => {
if (e.key === 'Enter')
this.deleteAccount();
}
document.getElementById('deleteButton').onclick = this.deleteAccount;
}
passwordInit() {
document.getElementById('currentPasswordInput').onkeydown = e => {
if (e.key === 'Enter')
this.savePassword();
};
document.getElementById('newPasswordInput').onkeydown = e => {
if (e.key === 'Enter')
this.savePassword();
};
document.getElementById('newPassword2Input').onkeydown = e => {
if (e.key === 'Enter')
this.savePassword();
};
document.getElementById('passwordSave').onclick = this.savePassword;
}
usernameInit() {
const usernameInput = document.getElementById('usernameInput');
const usernameSave = document.getElementById('usernameSave');
usernameInput.oninput = e => {
const value = e.target.value;
if (value != client.me.username && value.length)
usernameSave.classList.remove('disabled');
else
usernameSave.classList.add('disabled');
}
usernameSave.onclick = _ => this.saveUsername();
}
avatarInit() {
const avatar = document.getElementById('avatar');
const avatarInput = document.getElementById('avatarInput');
const avatarUpload = document.getElementById('avatarUpload');
const avatarDelete = document.getElementById('avatarDelete');
avatar.onclick = _ => avatarInput.click();
avatarInput.onchange = function () {
const selectedFile = this.files[0];
if (!selectedFile)
return;
avatar.src = URL.createObjectURL(selectedFile);
avatarUpload.classList.remove('d-none');
}
avatarUpload.onclick = _ => this.saveAvatar();
avatarDelete.onclick = _ => this.deleteAvatar();
}
async displayAvatar() {
let avatar = document.getElementById('avatar');
avatar.src = client.me.avatar_url + '?t=' + new Date().getTime();
}
async savePassword() {
const currentPasswordInput = document.getElementById('currentPasswordInput');
const currentPassword = currentPasswordInput.value;
const currentPasswordDetail = document.getElementById('currentPasswordDetail');
const newPasswordInput = document.getElementById('newPasswordInput');
const newPassword = newPasswordInput.value;
const newPasswordDetail = document.getElementById('newPasswordDetail');
const newPassword2Input = document.getElementById('newPassword2Input');
const newPassword2 = newPassword2Input.value;
const newPassword2Detail = document.getElementById('newPassword2Detail');
const passwordDetail = document.getElementById('passwordDetail');
clearElements('innerHTML', [currentPasswordDetail,
newPasswordDetail,
newPassword2Detail,
passwordDetail
]);
currentPasswordInput.classList.remove('is-invalid');
newPasswordInput.classList.remove('is-invalid');
newPassword2Input.classList.remove('is-invalid');
if (!currentPassword.length) {
currentPasswordDetail.innerHTML = lang.get('errorEmptyField');
currentPasswordInput.classList.add('is-invalid');
}
if (!newPassword.length) {
newPasswordDetail.innerHTML = lang.get('errorEmptyField');
newPasswordInput.classList.add('is-invalid');
}
if (!newPassword2.length) {
newPassword2Detail.innerHTML = lang.get('errorEmptyField');
newPassword2Input.classList.add('is-invalid');
}
if (!currentPassword.length || !newPassword.length || !newPassword2.length)
return;
const error = await client.account.updatePassword(currentPassword, newPassword, newPassword2);
if (!error) {
passwordDetail.classList.remove('text-danger');
passwordDetail.classList.add('text-success');
passwordDetail.innerHTML = lang.get('passwordSaved');
setTimeout(_ => passwordDetail.innerHTML = '', 3000);
clearElements('value', [currentPasswordInput, newPasswordInput, newPassword2Input]);
} else {
passwordDetail.classList.add('text-danger');
passwordDetail.classList.remove('text-success');
fill_errors(error, 'innerHTML');
if (error.currentPasswordDetail)
currentPasswordInput.classList.add('is-invalid');
if (error.newPasswordDetail)
newPasswordInput.classList.add('is-invalid');
if (error.newPassword2Detail)
newPassword2Input.classList.add('is-invalid');
}
}
async saveUsername()
{
const usernameInput = document.getElementById('usernameInput');
const username = usernameInput.value;
const usernameDetail = document.getElementById('usernameDetail');
if (!username.length || username === client.me.username)
return;
const error = await client.account.updateUsername(username);
if (!error) {
usernameDetail.classList.remove('text-danger', 'd-none');
usernameDetail.classList.add('text-success');
usernameDetail.innerHTML = 'Username Saved.';
setTimeout(_ => usernameDetail.add('d-none'), 2000);
document.getElementById('usernameSave').classList.add('disabled');
} else {
usernameDetail.classList.remove('text-success', 'd-none');
usernameDetail.classList.add('text-danger');
usernameDetail.innerHTML = error;
document.getElementById('usernameSave').classList.add('disabled');
console.log(error);
}
}
async saveAvatar()
{
const avatarInput = document.getElementById('avatarInput');
const selectedFile = avatarInput.files[0];
const avatarDetail = document.getElementById('avatarDetail');
if (!selectedFile)
return;
if (selectedFile.size > this.PROFILE_PICTURE_MAX_SIZE) {
avatarDetail.classList.remove('text-success');
avatarDetail.classList.add('text-danger');
avatarDetail.innerHTML = 'Image is too large.'; //to translate
return;
}
const error = await client.me.changeAvatar(selectedFile);
if (!error) {
avatarDetail.classList.remove('text-danger');
avatarDetail.classList.add('text-success');
avatarDetail.innerHTML = 'Avatar saved.'; //to translate
setTimeout(_ => avatarDetail.innerHTML = '', 2000);
document.getElementById('avatarDelete').classList.remove('d-none');
document.getElementById('avatarUpload').classList.add('d-none');
avatarInput.value = null;
} else {
avatarDetail.classList.remove('text-success');
avatarDetail.classList.add('text-danger');
avatarDetail.innerHTML = error.avatar[0];
document.getElementById('avatarUpload').classList.add('d-none');
avatarInput.value = null;
console.log(error);
}
this.displayAvatar();
}
async deleteAvatar() {
const avatarDetail = document.getElementById('avatarDetail');
const error = await client.me.deleteAvatar();
if (!error) {
avatarDetail.classList.remove('text-danger');
avatarDetail.classList.add('text-success');
avatarDetail.innerHTML = 'Avatar deleted.'; //to translate
setTimeout(_ => avatarDetail.innerHTML = '', 2000);
document.getElementById('avatarDelete').classList.add('d-none');
} else {
avatarDetail.classList.remove('text-success');
avatarDetail.classList.add('text-danger');
avatarDetail.innerHTML = 'Something went wrong.'; //to translate
}
this.displayAvatar();
}
async deleteAccount() {
const passwordInput = document.getElementById('deleteInput');
const password = passwordInput.value;
const passwordDetail = document.getElementById('deleteDetail');
passwordInput.classList.remove('is-invalid');
passwordDetail.innerHTML = '';
if (!password.length) {
passwordInput.classList.add('is-invalid');
passwordDetail.innerHTML = lang.get('errorEmptyField');
return;
}
const error = await client.account.delete(password);
if (!error) {
passwordDetail.classList.replace('text-danger', 'text-success');
passwordDetail.innerHTML = 'Account successfully deleted.';
setTimeout(_ => {
bootstrap.Modal.getInstance(document.getElementById('deleteModal')).hide();
navigateTo('/login');
}, 2000);
return;
}
passwordInput.classList.add('is-invalid');
passwordDetail.innerHTML = error['password'];
}
async getHtml()
{
const avatarUnchanged = client.me.avatar_url === '/static/avatars/default.avif';
return /* HTML */ `
<div class='container-fluid col-lg-8 d-flex rounded border border-2 bg-light-subtle py-3'>
<div class='row col-4 bg-body-tertiary border rounded p-2 m-2 d-flex justify-content-center align-items-center'>
<h2 class='border-bottom'>Avatar</h2>
<img id='avatar' class='rounded p-0' src=${client.me.avatar_url} style='cursor: pointer;'>
<input id='avatarInput' class='d-none' type='file' accept='image/*'>
<div class='d-flex gap-2 mt-1 px-0'>
<span class='my-auto ms-1 me-auto' id='avatarDetail'></span>
<button class='btn btn-primary d-none' id='avatarUpload'>Save</button>
<button class='btn btn-danger${avatarUnchanged ? ' d-none' : ''}' id='avatarDelete'>Delete</button>
</div>
</div>
<div class='flex-grow-1'>
<h1 class='border-bottom ps-1 mb-3'>Account settings</h1>
<div class='d-flex flex-column gap-2'>
<div>
<div class='input-group'>
<div class='form-floating'>
<input type='text' class='form-control' id='usernameInput' placeholder='username' value=${client.me.username}>
<label for='usernameInput'>Username</label>
</div>
<button class='input-group-text btn btn-success disabled' id='usernameSave'>Save</button>
</div>
<span class='form-text' id='usernameDetail'></span>
</div>
<div class='flex-grow-1 p-1 border rounded bg-body-tertiary'>
<h5 class='ps-1 border-bottom'>Change password</h5>
<div class='d-flex flex-column gap-1'>
<div class='form-floating'>
<input type='password' class='form-control' id='currentPasswordInput' placeholder='Current password'>
<label for='currentPasswordInput'>Current password</label>
</div>
<span class='text-danger ps-1' id='currentPasswordDetail'></span>
<div class='form-floating'>
<input type='password' class='form-control' id='newPasswordInput' placeholder='New password'>
<label for='newPassword2Input'>New password</label>
</div>
<span class='text-danger ps-1' id='newPasswordDetail'></span>
<div class='form-floating'>
<input type='password' class='form-control' id='newPassword2Input' placeholder='Repeat new password'>
<label for='newPassword2Input'>Repeat new password</label>
</div>
<span class='text-danger ps-1' id='newPassword2Detail'></span>
<div class='d-flex flex-row'>
<span class='ps-1 my-auto text-danger' id='passwordDetail'></span>
<button class='btn btn-success ms-auto' id='passwordSave'>Save</button>
</div>
</div>
</div>
<button class='btn btn-outline-danger' data-bs-toggle='modal' data-bs-target='#deleteModal'>Delete account</button>
<div class='modal fade' id='deleteModal' tabindex='-1'>
<div class='modal-dialog'>
<div class='modal-content'>
<div class='modal-header bg-danger bg-gradient'>
<h1 class='modal-title fs-5'>Are you sure ? This action is permanent !!</h1>
<button class='btn-close' data-bs-dismiss='modal'></button>
</div>
<div class='modal-body'>
<h6 class='form-label'>Please enter your password to confirm deletion:</h6>
<input type='password' class='form-control' id='deleteInput' placeholder='Password'>
<span class='form-text text-danger' id='deleteDetail'></span>
</div>
<div class='modal-footer'>
<button type='button' class='btn btn-secondary' data-bs-dismiss='modal'>Cancel</button>
<button type='button' class='btn btn-danger' id='deleteButton'>Delete account</button>
</div>
</div>
</div>
</div>
</div>
</div>
`;
}
}