settings: avatar & username (& little stuff too)

This commit is contained in:
AdrienLSH
2024-03-11 15:21:47 +01:00
parent 4b3be86b2e
commit 4238b3d2be
19 changed files with 302 additions and 214 deletions

View File

@ -1,11 +1,3 @@
#app #avatar {
max-height: 10em;
max-width: 10em;
min-height: 6em;
min-width: 6em;
}
#popup {
position: fixed;
font-size: 1.2em;

View File

@ -1,11 +0,0 @@
#app * {
font-size: 30px;
}
#app #main
{
width: 60%;
display: flex;
flex-direction: column;
}

View File

@ -48,41 +48,24 @@ class Account
}
/**
* Get account data (username)
* @returns {?Promise<Object>}
*/
async get()
* @param {String} newUsername
* @returns {?Promise<Object>}
*/
async updateUsername(newUsername)
{
let response = await this.client._get("/api/accounts/edit");
let response_data = await response.json();
const data = {
username: newUsername
};
const response = await this.client._patch_json(`/api/accounts/update_profile`, data);
const respondeData = await response.json();
if (response.status === 403)
{
this.client._update_logged(false);
if (response.status === 200) {
this.client.me.username = respondeData.username;
document.getElementById('navbarDropdownButton').innerHTML = respondeData.username;
document.getElementById('myProfileLink').href = '/profiles/' + respondeData.username;
return null;
}
return response_data;
}
/**
*
* @param {*} data
* @param {Number} password
* @returns {?Object}
*/
async update(data, password)
{
data.current_password = password;
let response = await this.client._patch_json("/api/accounts/edit", data);
let response_data = await response.json();
if (response.status === 403)
{
this.client._update_logged(false);
return null;
}
return response_data;
return respondeData['authorize'] || respondeData['detail'] || respondeData['username']?.join(' ') || 'Error.';
}
}

View File

@ -14,15 +14,35 @@ class MyProfile extends Profile
/**
*
* @param {*} form_data
* @returns {Promise<Object>}
* @param {File} selectedFile
* @returns {Promise<Response>}
*/
async change_avatar(form_data)
async changeAvatar(selectedFile)
{
let response = await this.client._patch_file(`/api/profiles/settings`, form_data);
let response_data = await response.json();
const formData = new FormData();
formData.append('avatar', selectedFile);
return response_data;
const response = await this.client._patch_file(`/api/profiles/settings`, formData);
const responseData = await response.json();
if (response.ok) {
console.log('save', responseData);
this.avatar_url = responseData.avatar.substr(responseData.avatar.indexOf('static') - 1);
return null;
}
return responseData;
}
async deleteAvatar() {
const response = await this.client._delete('/api/profiles/settings');
const responseData = await response.json();
if (response.ok) {
console.log('delete', responseData);
this.avatar_url = responseData.avatar.substr(responseData.avatar.indexOf('static') - 1);
return null;
}
return responseData;
}
}

View File

@ -1,55 +1,82 @@
import { client, navigateTo } from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js";
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
import { client, navigateTo } from '../index.js';
import { clear, fill_errors } from '../utils/formUtils.js';
import AbstractAuthenticatedView from './abstracts/AbstractAuthenticatedView.js';
export default class extends AbstractAuthenticatedView
{
constructor(params)
{
super(params, "Settings");
super(params, 'Settings');
this.PROFILE_PICTURE_MAX_SIZE = 2 * 1024 * 1024; // 2MB
}
async postInit()
{
this.display_avatar();
document.getElementById("save-account-button").onclick = () => this.save_account();
document.getElementById("delete-account-button").onclick = () => this.delete_account();
document.getElementById("save-profile-button").onclick = () => this.save_profile();
this.avatarInit();
this.usernameInit();
// document.getElementById('delete-account-button').onclick = () => this.delete_account();
}
async display_avatar() {
let profile = await client.profiles.getProfile(client.me.username);
if (profile !== undefined || profile !== null) {
if (document.getElementById("avatar") != undefined)
document.getElementById("avatar").remove();
let avatar = document.createElement("img");
avatar.id = "avatar";
avatar.src = profile.avatar_url + '?t=' +new Date().getTime();
document.getElementsByClassName("avatar")[0].appendChild(avatar);
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();
console.log(avatar.src);
}
async delete_account()
{
let current_password = document.getElementById("current_password-input").value;
let current_password = document.getElementById('current_password-input').value;
let response_data = await client.account.delete(current_password);
if (response_data === null || response_data === "user deleted")
if (response_data === null || response_data === 'user deleted')
{
navigateTo("/login");
navigateTo('/login');
return;
}
clear("innerHTML", ["current_password-input"]);
fill_errors({"current_password-input": response_data.password}, "innerHTML");
clear('innerHTML', ['current_password-input']);
fill_errors({'current_password-input': response_data.password}, 'innerHTML');
}
async save_account()
{
let username = document.getElementById("username-input").value;
let new_password = document.getElementById("new_password-input").value;
let current_password = document.getElementById("current_password-input").value;
let username = document.getElementById('username-input').value;
let new_password = document.getElementById('new_password-input').value;
let current_password = document.getElementById('current_password-input').value;
let data = {};
@ -61,67 +88,139 @@ export default class extends AbstractAuthenticatedView
if (response_data === null)
{
navigateTo("/login");
navigateTo('/login');
return;
}
if (response_data === "data has been alterate")
response_data = {"save-account": "saved"};
if (response_data === 'data has been alterate')
response_data = {'save-account': 'saved'};
clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"]);
fill_errors(response_data, "innerHTML");
}
async save_profile()
{
let avatar = document.getElementById("avatar-input");
if (avatar.files[0] !== undefined)
{
if (avatar.files[0].size > this.PROFILE_PICTURE_MAX_SIZE) {
document.getElementById("save-profile").classList.add('text-danger');
document.getElementById("save-profile").innerHTML = "Image too large :/";
return;
}
let form_data = new FormData();
form_data.append("avatar", avatar.files[0]);
await client.me.change_avatar(form_data);
this.display_avatar();
}
document.getElementById("save-profile").classList.remove('text-danger');
document.getElementById("save-profile").innerHTML = "Saved";
clear('innerHTML', ['username', 'new_password', 'current_password', 'save-account', 'delete-account']);
fill_errors(response_data, 'innerHTML');
}
async getHtml()
{
return /* HTML */ `
<link rel="stylesheet" href="/static/css/settings.css">
<h1>ME</h1>
<div id="main">
<div class="avatar">
</div>
<div class="account">
<h3>Account</h3>
<input type="text" placeholder="username" id="username-input" text=${client.me.username}>
<span id="username"></span>
<input type=password placeholder="new_password" id="new_password-input">
<span id="new_password"></span>
<input type=password placeholder="current_password" id="current_password-input">
<span id="current_password"></span>
async saveUsername()
{
const usernameInput = document.getElementById('usernameInput');
const username = usernameInput.value;
const usernameDetail = document.getElementById('usernameDetail');
<input type="button" value="Save Credentials" id="save-account-button">
<span id="save-account"></span>
<input type="button" value="Delete Account" id="delete-account-button">
<span id="delete-account"></span>
</div>
<div class="profile">
<h3>Profile</h3>
<input type="file" id="avatar-input" accept="image/*">
<input type="button" value="Save profile" id="save-profile-button">
<span id="save-profile"></span>
</div>
<a href="/logout" class="nav__link" data-link>Logout</a>
</div>
`;
}
if (!username.length || username === client.me.username)
return;
const error = await client.account.updateUsername(username);
if (!error) {
usernameDetail.classList.remove('text-danger');
usernameDetail.classList.add('text-success');
usernameDetail.innerHTML = 'Username Saved.';
setTimeout(_ => usernameDetail.innerHTML = '', 2000);
document.getElementById('usernameSave').classList.add('disabled');
} else {
usernameDetail.classList.remove('text-success');
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 getHtml()
{
const avatarUnchanged = client.me.avatar_url === '/static/avatars/default.avif';
return /* HTML */ `
<div class='container col-sm-10 col-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</h1>
<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>
</div>
`;
// <input class='form-control' type='text' placeholder='Username' id='username-input' value=${client.me.username}>
// <h1>Settings</h1>
// <input class='form-control d-inline-block' type='text' placeholder='Username' id='username-input' value=${client.me.username}>
// <span id='username'></span>
// <input type=password placeholder='New Password' id='new_password-input'>
// <span id='new_password'></span>
// <input type=password placeholder='Current Password' id='current_password-input'>
// <span id='current_password'></span>
// <input type='button' value='Save Credentials' id='save-account-button'>
// <span id='save-account'></span>
//
// <input type='button' value='Delete Account' id='delete-account-button'>
// <span id='delete-account'></span>
}
}