Merge pull request #172 from HeyPuter/settings

Implement the Settings App
This commit is contained in:
Nariman Jelveh
2024-03-24 21:41:01 -07:00
committed by GitHub
42 changed files with 1273 additions and 42 deletions

View File

@@ -0,0 +1,96 @@
/**
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// About
function UITabAbout(){
let h = ``;
h += `<div class="settings-content active" data-settings="about">`;
h += `<div class="about-container">`
h += `<div class="about">
<a href="https://puter.com" target="_blank" class="logo"><img src="/images/logo.png"></a>
<p class="description">${i18n('puter_description')}</p>
<p class="links">
<a href="mailto:hey@puter.com" target="_blank">hey@puter.com</a>
<span style="color: #CCC;">•</span>
<a href="https://docs.puter.com" target="_blank">${i18n('developers')}</a>
<span style="color: #CCC;">•</span>
<a href="https://status.puter.com" target="_blank">${i18n('status')}</a>
<span style="color: #CCC;">•</span>
<a href="https://puter.com/terms" target="_blank">${i18n('terms')}</a>
<span style="color: #CCC;">•</span>
<a href="https://puter.com/privacy" target="_blank">${i18n('privacy')}</a>
<span style="color: #CCC;">•</span>
<a href="#" class="show-credits">${i18n('credits')}</a>
</p>
<div class="social-links">
<a href="https://twitter.com/HeyPuter/" target="_blank">
<svg viewBox="0 0 24 24" aria-hidden="true" style="opacity: 0.7;"><g><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path></g></svg>
</a>
<a href="https://github.com/HeyPuter/" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48">
<g transform="translate(0, 0)">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#5a606b" d="M24,0.6c-13.3,0-24,10.7-24,24c0,10.6,6.9,19.6,16.4,22.8 c1.2,0.2,1.6-0.5,1.6-1.2c0-0.6,0-2.1,0-4.1c-6.7,1.5-8.1-3.2-8.1-3.2c-1.1-2.8-2.7-3.5-2.7-3.5c-2.2-1.5,0.2-1.5,0.2-1.5 c2.4,0.2,3.7,2.5,3.7,2.5c2.1,3.7,5.6,2.6,7,2c0.2-1.6,0.8-2.6,1.5-3.2c-5.3-0.6-10.9-2.7-10.9-11.9c0-2.6,0.9-4.8,2.5-6.4 c-0.2-0.6-1.1-3,0.2-6.4c0,0,2-0.6,6.6,2.5c1.9-0.5,4-0.8,6-0.8c2,0,4.1,0.3,6,0.8c4.6-3.1,6.6-2.5,6.6-2.5c1.3,3.3,0.5,5.7,0.2,6.4 c1.5,1.7,2.5,3.8,2.5,6.4c0,9.2-5.6,11.2-11,11.8c0.9,0.7,1.6,2.2,1.6,4.4c0,3.2,0,5.8,0,6.6c0,0.6,0.4,1.4,1.7,1.2 C41.1,44.2,48,35.2,48,24.6C48,11.3,37.3,0.6,24,0.6z">
</path>
</g>
</svg>
</a>
<a href="https://discord.gg/PQcx7Teh8u" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48"><g transform="translate(0, 0)"><path d="M19.837,20.3a2.562,2.562,0,0,0,0,5.106,2.562,2.562,0,0,0,0-5.106Zm8.4,0a2.562,2.562,0,1,0,2.346,2.553A2.45,2.45,0,0,0,28.232,20.3Z" fill="#444444" data-color="color-2"></path> <path d="M39.41,1H8.59A4.854,4.854,0,0,0,4,6V37a4.482,4.482,0,0,0,4.59,4.572H34.672l-1.219-4.255L36.4,40.054,39.18,42.63,44,47V6A4.854,4.854,0,0,0,39.41,1ZM30.532,31.038s-.828-.989-1.518-1.863a7.258,7.258,0,0,0,4.163-2.737A13.162,13.162,0,0,1,30.532,27.8a15.138,15.138,0,0,1-3.335.989,16.112,16.112,0,0,1-5.957-.023,19.307,19.307,0,0,1-3.381-.989,13.112,13.112,0,0,1-2.622-1.357,7.153,7.153,0,0,0,4.025,2.714c-.69.874-1.541,1.909-1.541,1.909-5.083-.161-7.015-3.5-7.015-3.5a30.8,30.8,0,0,1,3.312-13.409,11.374,11.374,0,0,1,6.463-2.415l.23.276a15.517,15.517,0,0,0-6.049,3.013s.506-.276,1.357-.667a17.272,17.272,0,0,1,5.221-1.449,2.266,2.266,0,0,1,.391-.046,19.461,19.461,0,0,1,4.646-.046A18.749,18.749,0,0,1,33.2,15.007a15.307,15.307,0,0,0-5.727-2.921l.322-.368a11.374,11.374,0,0,1,6.463,2.415A30.8,30.8,0,0,1,37.57,27.542S35.615,30.877,30.532,31.038Z" fill="#444444"></path></g></svg> </a>
<a href="https://www.linkedin.com/company/puter/" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48">
<g transform="translate(0, 0)">
<path fill="#5a606b" d="M46,0H2C0.9,0,0,0.9,0,2v44c0,1.1,0.9,2,2,2h44c1.1,0,2-0.9,2-2V2C48,0.9,47.1,0,46,0z M14.2,40.9H7.1V18 h7.1V40.9z M10.7,14.9c-2.3,0-4.1-1.8-4.1-4.1c0-2.3,1.8-4.1,4.1-4.1c2.3,0,4.1,1.8,4.1,4.1C14.8,13,13,14.9,10.7,14.9z M40.9,40.9 h-7.1V29.8c0-2.7,0-6.1-3.7-6.1c-3.7,0-4.3,2.9-4.3,5.9v11.3h-7.1V18h6.8v3.1h0.1c0.9-1.8,3.3-3.7,6.7-3.7c7.2,0,8.5,4.7,8.5,10.9 V40.9z">
</path>
</g>
</svg>
</a>
</div>
</div>
<div class="version"></div>
<dialog class="credits">
<div class="credit-content">
<p style="margin: 0; font-size: 18px; text-align: center;">${i18n('oss_code_and_content')}</p>
<div style="max-height: 300px; overflow-y: scroll;">
<ul style="padding-left: 25px; padding-top:15px;">
<li>FileSaver.js <a target="_blank" href="https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md">${i18n('license')}</a></li>
<li>html-entities <a target="_blank" href="https://github.com/mdevils/html-entities/blob/master/LICENSE">${i18n('license')}</a></li>
<li>iro.js <a target="_blank" href="https://github.com/jaames/iro.js/blob/master/LICENSE.txt">${i18n('license')}</a></li>
<li>jQuery <a target="_blank" href="https://jquery.org/license/">${i18n('license')}</a></li>
<li>jQuery-dragster <a target="_blank" href="https://github.com/catmanjan/jquery-dragster/blob/master/LICENSE">${i18n('license')}</a></li>
<li>jQuery-menu-aim <a target="_blank" href="https://github.com/kamens/jQuery-menu-aim?tab=readme-ov-file#faq">${i18n('license')}</a></li>
<li>jQuery UI <a target="_blank" href="https://jquery.org/license/">${i18n('license')}</a></li>
<li>lodash <a target="_blank" href="https://lodash.com/license">${i18n('license')}</a></li>
<li>mime <a target="_blank" href="https://github.com/broofa/mime/blob/main/LICENSE">${i18n('license')}</a></li>
<li>qrcodejs <a target="_blank" href="https://github.com/davidshimjs/qrcodejs/blob/master/LICENSE">${i18n('license')}</a></li>
<li>Selection <a target="_blank" href="https://github.com/simonwep/selection/blob/master/LICENSE">${i18n('license')}</a></li>
<li>socket.io <a target="_blank" href="https://github.com/socketio/socket.io/blob/main/LICENSE">${i18n('license')}</a></li>
<li>Wallpaper by <a target="_blank" href="https://unsplash.com/@fakurian?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Milad Fakurian</a> on <a target="_blank" href="https://unsplash.com/photos/blue-orange-and-yellow-wallpaper-E8Ufcyxz514?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a></li>
</ul>
</div>
</div>
</dialog>
`;
h += `</div>`;
h += `</div>`;
return h;
}
export default UITabAbout;

View File

@@ -0,0 +1,83 @@
/**
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import UIWindow from '../UIWindow.js'
import UIWindowFinalizeUserDeletion from './UIWindowFinalizeUserDeletion.js'
async function UIWindowConfirmUserDeletion(options){
return new Promise(async (resolve) => {
options = options ?? {};
let h = '';
h += `<div style="padding: 20px;">`;
h += `<div class="generic-close-window-button disable-user-select"> &times; </div>`;
h += `<img src="${window.icons['danger.svg']}" class="account-deletion-confirmation-icon">`;
h += `<p class="account-deletion-confirmation-prompt">${i18n('confirm_delete_user')}</p>`;
h += `<button class="button button-block button-danger proceed-with-user-deletion">${i18n('proceed_with_account_deletion')}</button>`;
h += `<button class="button button-block button-secondary cancel-user-deletion">${i18n('cancel')}</button>`;
h += `</div>`;
const el_window = await UIWindow({
title: i18n('confirm_delete_user_title'),
icon: null,
uid: null,
is_dir: false,
body_content: h,
has_head: false,
selectable_body: false,
draggable_body: false,
allow_context_menu: false,
is_draggable: true,
is_resizable: false,
is_droppable: false,
init_center: true,
allow_native_ctxmenu: true,
allow_user_select: true,
backdrop: true,
onAppend: function(el_window){
},
width: 500,
dominant: true,
window_css: {
height: 'initial',
padding: '0',
border: 'none',
boxShadow: '0 0 10px rgba(0,0,0,.2)',
borderRadius: '5px',
backgroundColor: 'white',
color: 'black',
},
});
$(el_window).find('.generic-close-window-button').on('click', function(){
$(el_window).close();
});
$(el_window).find('.cancel-user-deletion').on('click', function(){
$(el_window).close();
});
$(el_window).find('.proceed-with-user-deletion').on('click', function(){
UIWindowFinalizeUserDeletion();
$(el_window).close();
});
})
}
export default UIWindowConfirmUserDeletion;

View File

@@ -0,0 +1,152 @@
/**
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import UIWindow from '../UIWindow.js'
async function UIWindowFinalizeUserDeletion(options){
return new Promise(async (resolve) => {
options = options ?? {};
let h = '';
// if user is temporary, ask them to type in 'confirm' to delete their account
if(user.is_temp){
h += `<div style="padding: 20px;">`;
h += `<div class="generic-close-window-button disable-user-select"> &times; </div>`;
h += `<img src="${window.icons['danger.svg']}" class="account-deletion-confirmation-icon">`;
h += `<p class="account-deletion-confirmation-prompt">${i18n('type_confirm_to_delete_account')}</p>`;
// error message
h += `<div class="error-message"></div>`;
// input field
h += `<input type="text" class="confirm-temporary-user-deletion" placeholder="${i18n('type_confirm_to_delete_account')}">`;
h += `<button class="button button-block button-danger proceed-with-user-deletion">${i18n('delete_account')}</button>`;
h += `<button class="button button-block button-secondary cancel-user-deletion">${i18n('cancel')}</button>`;
h += `</div>`;
}
// otherwise ask for password
else{
h += `<div style="padding: 20px;">`;
h += `<div class="generic-close-window-button disable-user-select"> &times; </div>`;
h += `<img src="${window.icons['danger.svg']}" class="account-deletion-confirmation-icon">`;
h += `<p class="account-deletion-confirmation-prompt">${i18n('enter_password_to_confirm_delete_user')}</p>`;
// error message
h += `<div class="error-message"></div>`;
// input field
h += `<input type="password" class="confirm-user-deletion-password" placeholder="${i18n('current_password')}">`;
h += `<button class="button button-block button-danger proceed-with-user-deletion">${i18n('delete_account')}</button>`;
h += `<button class="button button-block button-secondary cancel-user-deletion">${i18n('cancel')}</button>`;
h += `</div>`;
}
const el_window = await UIWindow({
title: i18n('confirm_delete_user_title'),
icon: null,
uid: null,
is_dir: false,
body_content: h,
has_head: false,
selectable_body: false,
draggable_body: false,
allow_context_menu: false,
is_draggable: true,
is_resizable: false,
is_droppable: false,
init_center: true,
allow_native_ctxmenu: true,
allow_user_select: true,
backdrop: true,
onAppend: function(el_window){
},
width: 500,
dominant: false,
window_css: {
height: 'initial',
padding: '0',
border: 'none',
boxShadow: '0 0 10px rgba(0,0,0,.2)',
}
});
$(el_window).find('.generic-close-window-button').on('click', function(){
$(el_window).close();
});
$(el_window).find('.cancel-user-deletion').on('click', function(){
$(el_window).close();
});
$(el_window).find('.proceed-with-user-deletion').on('click', function(){
$(el_window).find('.error-message').hide();
// if user is temporary, check if they typed 'confirm'
if(user.is_temp){
if($(el_window).find('.confirm-temporary-user-deletion').val() !== 'confirm'){
$(el_window).find('.error-message').html(i18n('type_confirm_to_delete_account'), false);
$(el_window).find('.error-message').show();
return;
}
}
// otherwise, check if password is correct
else{
if($(el_window).find('.confirm-user-deletion-password').val() === ''){
$(el_window).find('.error-message').html(i18n('all_fields_required'), false);
$(el_window).find('.error-message').show();
return;
}
}
// delete user
$.ajax({
url: api_origin + "/delete-own-user",
type: 'POST',
async: true,
contentType: "application/json",
headers: {
"Authorization": "Bearer " + auth_token
},
data: JSON.stringify({
password: $(el_window).find('.confirm-user-deletion-password').val(),
}),
statusCode: {
401: function () {
logout();
},
400: function(){
$(el_window).find('.error-message').html(i18n('incorrect_password'));
$(el_window).find('.error-message').show();
}
},
success: function(data){
if(data.success){
// mark user as deleted
window.user.deleted = true;
// log user out
logout();
}
else{
$(el_window).find('.error-message').html(data.error);
$(el_window).find('.error-message').show();
}
}
});
});
})
}
export default UIWindowFinalizeUserDeletion;

View File

@@ -0,0 +1,352 @@
/**
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import UIWindow from '../UIWindow.js'
import UIWindowChangePassword from '../UIWindowChangePassword.js'
// import UIWindowChangeEmail from './UIWindowChangeEmail.js'
// import UIWindowDeleteAccount from './UIWindowDeleteAccount.js'
import UIWindowChangeUsername from '../UIWindowChangeUsername.js'
import changeLanguage from "../../i18n/i18nchangeLanguage.js"
import UIWindowConfirmUserDeletion from './UIWindowConfirmUserDeletion.js';
import UITabAbout from './UITabAbout.js';
async function UIWindowSettings(options){
return new Promise(async (resolve) => {
options = options ?? {};
let h = '';
h += `<div class="settings-container">`;
h += `<div class="settings">`;
// side bar
h += `<div class="settings-sidebar disable-user-select">`;
h += `<div class="settings-sidebar-item disable-user-select active" data-settings="about" style="background-image: url(${icons['logo-outline.svg']});">${i18n('about')}</div>`;
h += `<div class="settings-sidebar-item disable-user-select" data-settings="usage" style="background-image: url(${icons['speedometer-outline.svg']});">${i18n('usage')}</div>`;
h += `<div class="settings-sidebar-item disable-user-select" data-settings="account" style="background-image: url(${icons['user.svg']});">${i18n('account')}</div>`;
h += `<div class="settings-sidebar-item disable-user-select" data-settings="language" style="background-image: url(${icons['language.svg']});">${i18n('language')}</div>`;
h += `</div>`;
// content
h += `<div class="settings-content-container">`;
// About
h += UITabAbout();
// Usage
h += `<div class="settings-content" data-settings="usage">`;
h += `<h1>Usage</h1>`;
h += `<div class="driver-usage">
<h3 style="margin-bottom: 5px; font-size: 14px;">${i18n('storage_usage')}</h3>
<div style="font-size: 13px; margin-bottom: 3px;">
<span id="storage-used"></span>
<span> used of </span>
<span id="storage-capacity"></span>
</div>
<div id="storage-bar-wrapper">
<span id="storage-used-percent"></span>
<div id="storage-bar"></div>
</div>
</div>`
h += `</div>`;
// Account
h += `<div class="settings-content" data-settings="account">`;
h += `<h1>${i18n('account')}</h1>`;
// change password button
h += `<div class="settings-card">`;
h += `<strong>${i18n('password')}</strong>`;
h += `<div style="flex-grow:1;">`;
h += `<button class="button change-password" style="float:right;">${i18n('change_password')}</button>`;
h += `</div>`;
h += `</div>`;
// change username button
h += `<div class="settings-card">`;
h += `<div>`;
h += `<strong style="display:block;">${i18n('username')}</strong>`;
h += `<span class="username" style="display:block; margin-top:5px;">${user.username}</span>`;
h += `</div>`;
h += `<div style="flex-grow:1;">`;
h += `<button class="button change-username" style="float:right;">${i18n('change_username')}</button>`;
h += `</div>`
h += `</div>`;
// change email button
if(user.email){
h += `<div class="settings-card">`;
h += `<div>`;
h += `<strong style="display:block;">${i18n('email')}</strong>`;
h += `<span style="display:block; margin-top:5px;">${user.email}</span>`;
h += `</div>`;
h += `<div style="flex-grow:1;">`;
h += `<button class="button change-email" style="margin-bottom: 10px; float:right;">${i18n('change_email')}</button>`;
h += `</div>`;
h += `</div>`;
}
// 'Delete Account' button
h += `<div class="settings-card settings-card-danger">`;
h += `<strong style="display: inline-block;">${i18n("delete_account")}</strong>`;
h += `<div style="flex-grow:1;">`;
h += `<button class="button button-danger delete-account" style="float:right;">${i18n("delete_account")}</button>`;
h += `</div>`;
h += `</div>`;
h += `</div>`;
// Language
h += `<div class="settings-content" data-settings="language">`;
h += `<h1>${i18n('language')}</h1>`;
// search
h += `<div class="search-container" style="margin-bottom: 10px;">`;
h += `<input type="text" class="search" placeholder="Search">`;
h += `</div>`;
// list of languages
const available_languages = listSupportedLanguages();
h += `<div class="language-list">`;
for (let lang of available_languages) {
h += `<div class="language-item ${window.locale === lang.code ? 'active': ''}" data-lang="${lang.code}" data-english-name="${html_encode(lang.english_name)}">${lang.name}</div>`;
}
h += `</div>`;
h += `</div>`;
h += `</div>`;
h += `</div>`;
h += `</div>`;
h += ``;
const el_window = await UIWindow({
title: 'Settings',
app: 'settings',
single_instance: true,
icon: null,
uid: null,
is_dir: false,
body_content: h,
has_head: true,
selectable_body: false,
allow_context_menu: false,
is_resizable: false,
is_droppable: false,
init_center: true,
allow_native_ctxmenu: true,
allow_user_select: true,
backdrop: false,
width: 800,
height: 500,
height: 'auto',
dominant: true,
show_in_taskbar: false,
draggable_body: false,
onAppend: function(this_window){
},
window_class: 'window-settings',
body_css: {
width: 'initial',
height: '100%',
overflow: 'auto'
}
});
$.ajax({
url: api_origin + "/drivers/usage",
type: 'GET',
async: true,
contentType: "application/json",
headers: {
"Authorization": "Bearer " + auth_token
},
statusCode: {
401: function () {
logout();
},
},
success: function (res) {
let h = ''; // Initialize HTML string for driver usage bars
// Loop through user services
res.user.forEach(service => {
const { monthly_limit, monthly_usage } = service;
let usageDisplay = ``;
if (monthly_limit !== null) {
let usage_percentage = (monthly_usage / monthly_limit * 100).toFixed(0);
usage_percentage = usage_percentage > 100 ? 100 : usage_percentage; // Cap at 100%
usageDisplay = `
<div class="driver-usage" style="margin-bottom: 10px;">
<h3 style="margin-bottom: 5px; font-size: 14px;">${service.service['driver.interface']} (${service.service['driver.method']}):</h3>
<span style="font-size: 13px; margin-bottom: 3px;">${monthly_usage} used of ${monthly_limit}</span>
<div class="usage-progbar-wrapper" style="width: 100%;">
<div class="usage-progbar" style="width: ${usage_percentage}%;"><span class="usage-progbar-percent">${usage_percentage}%</span></div>
</div>
</div>
`;
}
else {
usageDisplay = `
<div class="driver-usage" style="margin-bottom: 10px;">
<h3 style="margin-bottom: 5px; font-size: 14px;">${service.service['driver.interface']} (${service.service['driver.method']}):</h3>
<span style="font-size: 13px; margin-bottom: 3px;">${i18n('usage')}: ${monthly_usage} (${i18n('unlimited')})</span>
</div>
`;
}
h += usageDisplay;
});
// Append driver usage bars to the container
$('.settings-content[data-settings="usage"]').append(`<div class="driver-usage-container">${h}</div>`);
}
})
// df
$.ajax({
url: api_origin + "/df",
type: 'GET',
async: true,
contentType: "application/json",
headers: {
"Authorization": "Bearer " + auth_token
},
statusCode: {
401: function () {
logout();
},
},
success: function (res) {
let usage_percentage = (res.used / res.capacity * 100).toFixed(0);
usage_percentage = usage_percentage > 100 ? 100 : usage_percentage;
$('#storage-used').html(byte_format(res.used));
$('#storage-capacity').html(byte_format(res.capacity));
$('#storage-used-percent').html(usage_percentage + '%');
$('#storage-bar').css('width', usage_percentage + '%');
if (usage_percentage >= 100) {
$('#storage-bar').css({
'border-top-right-radius': '3px',
'border-bottom-right-radius': '3px',
});
}
}
})
// version
$.ajax({
url: api_origin + "/version",
type: 'GET',
async: true,
contentType: "application/json",
headers: {
"Authorization": "Bearer " + auth_token
},
statusCode: {
401: function () {
logout();
},
},
success: function (res) {
var d = new Date(0);
$('.version').html('Version: ' + res.version + ' &bull; ' + 'Server: ' + res.location + ' &bull; ' + 'Deployed: ' + new Date(res.deploy_timestamp));
}
})
$(el_window).find('.credits').on('click', function (e) {
if($(e.target).hasClass('credits')){
$('.credits').get(0).close();
}
});
$(el_window).find('.show-credits').on('click', function (e) {
$('.credits').get(0).showModal();
})
$(el_window).find('.change-password').on('click', function (e) {
UIWindowChangePassword();
})
$(el_window).find('.change-email').on('click', function (e) {
UIWindowChangeEmail();
})
$(el_window).find('.delete-account').on('click', function (e) {
UIWindowConfirmUserDeletion();
})
$(el_window).find('.change-username').on('click', function (e) {
UIWindowChangeUsername();
})
$(el_window).on('click', '.settings-sidebar-item', function(){
const $this = $(this);
const settings = $this.attr('data-settings');
const $container = $this.closest('.settings').find('.settings-content-container');
const $content = $container.find(`.settings-content[data-settings="${settings}"]`);
// add active class to sidebar item
$this.siblings().removeClass('active');
$this.addClass('active');
// add active class to content
$container.find('.settings-content').removeClass('active');
$content.addClass('active');
// if language, focus on search
if(settings === 'language'){
$content.find('.search').first().focus();
// make sure all language items are visible
$content.find('.language-item').show();
// empty search
$content.find('.search').val('');
}
})
$(el_window).on('click', '.language-item', function(){
const $this = $(this);
const lang = $this.attr('data-lang');
changeLanguage(lang);
$this.siblings().removeClass('active');
$this.addClass('active');
// make sure all other language items are visible
$this.closest('.language-list').find('.language-item').show();
})
$(el_window).on('input', '.search', function(){
const $this = $(this);
const search = $this.val().toLowerCase();
const $container = $this.closest('.settings').find('.settings-content-container');
const $content = $container.find('.settings-content.active');
const $list = $content.find('.language-list');
const $items = $list.find('.language-item');
$items.each(function(){
const $item = $(this);
const lang = $item.attr('data-lang');
const name = $item.text().toLowerCase();
const english_name = $item.attr('data-english-name').toLowerCase();
if(name.includes(search) || lang.includes(search) || english_name.includes(search)){
$item.show();
}else{
$item.hide();
}
})
});
resolve(el_window);
});
}
export default UIWindowSettings

View File

@@ -148,12 +148,13 @@ function UIContextMenu(options){
return false;
});
// initialize menuAim plugin (../libs/jquery.menu-aim.js)
// Initialize the menuAim plugin (../libs/jquery.menu-aim.js)
$(contextMenu).menuAim({
submenuSelector: ".context-menu-item-submenu",
submenuDirection: function(){
//if not submenu
// If not submenu
if(!options.is_submenu){
// if submenu left postiton is greater than main menu left position
// if submenu's left postiton is greater than main menu's left position
if($(contextMenu).offset().left + 2 * $(contextMenu).width() + 15 < window.innerWidth ){
return "right";
} else {
@@ -161,12 +162,10 @@ function UIContextMenu(options){
}
}
},
//activates item when mouse enters depending in mouse position and direction
// activates item when mouse enters depending on mouse position and direction
activate: function (e) {
//activate items
let item = $(e).closest('.context-menu-item');
// mark other items as inactive
$(contextMenu).find('.context-menu-item').removeClass('context-menu-item-active');
// mark this item as active
@@ -176,7 +175,6 @@ function UIContextMenu(options){
// mark this context menu as active
$(contextMenu).addClass('context-menu-active');
// activate submenu
// open submenu if applicable
if($(e).hasClass('context-menu-item-submenu')){

View File

@@ -34,7 +34,8 @@ import UIWindowQR from "./UIWindowQR.js"
import UIWindowRefer from "./UIWindowRefer.js"
import UITaskbar from "./UITaskbar.js"
import new_context_menu_item from "../helpers/new_context_menu_item.js"
import ChangeLanguage from "../i18n/i18nChangeLanguage.js"
import changeLanguage from "../i18n/i18nchangeLanguage.js"
import UIWindowSettings from "./Settings/UIWindowSettings.js"
async function UIDesktop(options){
let h = '';
@@ -1156,12 +1157,12 @@ $(document).on('click', '.user-options-menu-btn', async function(e){
// -------------------------------------------
// Load available languages
// -------------------------------------------
const supportedLanguagesItems = ListSupportedLanguages().map(lang => {
const supportedLanguagesItems = listSupportedLanguages().map(lang => {
return {
html: lang.name,
icon: window.locale === lang.code ? '✓' : '',
onClick: async function(){
ChangeLanguage(lang.code);
changeLanguage(lang.code);
}
}
});
@@ -1181,33 +1182,15 @@ $(document).on('click', '.user-options-menu-btn', async function(e){
UIWindowMyWebsites();
}
},
//--------------------------------------------------
// Change Username
//--------------------------------------------------
{
html: i18n('change_username'),
onClick: async function(){
UIWindowChangeUsername();
}
},
//--------------------------------------------------
// Change Password
//--------------------------------------------------
{
html: i18n('change_password'),
html: i18n('settings'),
onClick: async function(){
UIWindowChangePassword();
UIWindowSettings();
}
},
//--------------------------------------------------
// Change Language
//--------------------------------------------------
{
html: i18n('change_language'),
items: supportedLanguagesItems
},
//--------------------------------------------------
// Contact Us
//--------------------------------------------------
@@ -1288,10 +1271,7 @@ $(document).on('click', '.close-launch-popover', function(){
});
$(document).on('click', '.toolbar-puter-logo', function(){
// launch the about app
launch_app({name: 'about', window_options:{
single_instance: true,
}});
UIWindowSettings();
})
$(document).on('click', '.user-options-create-account-btn', async function(e){

View File

@@ -2138,7 +2138,7 @@ label {
font: 14px "Helvetica Neue", Sans-Serif;
border: none !important;
backdrop-filter: blur(3px);
filter: drop-shadow(0 0 3px rgba(0,0,0,.455));
filter: drop-shadow(0 0 3px rgba(0, 0, 0, .455));
}
.arrow {
@@ -3254,4 +3254,359 @@ label {
justify-content: center;
align-items: center;
display: none;
}
/*!
* ==================================================
* Settings
* ==================================================
*/
.settings-container {
display: flex;
flex-direction: column;
height: 100%;
}
.settings {
display: flex;
flex-direction: row;
-webkit-font-smoothing: antialiased;
flex-grow: 1;
}
.settings-sidebar {
width: 200px;
background-color: #f9f9f9;
border-right: 1px solid #e0e0e0;
padding: 20px;
}
.settings-sidebar-item {
cursor: pointer;
border-radius: 4px;
padding: 10px;
margin-bottom: 15px;
background-repeat: no-repeat;
background-position: 10px center;
background-size: 25px;
padding-left: 45px;
font-size: 15px;
}
.settings-sidebar-item:hover {
background-color: #e8e8e8;
}
.settings-sidebar-item.active {
background-color: #e0e0e0;
}
.settings-content-container {
flex: 1;
padding: 20px 30px;
height: 500px;
overflow-y: scroll;
}
.settings-content {
display: none;
}
.settings-content[data-settings="about"] {
height: 100%;
}
.settings-content h1 {
font-size: 24px;
margin-bottom: 20px;
border-bottom: 1px solid #e0e0e0;
padding-bottom: 10px;
padding-left: 5px;
font-weight: 500;
}
.settings-content.active {
display: block;
}
.settings-content .about-container {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.settings-content[data-settings="about"] a {
color: #1663d4;
text-decoration: none;
font-size: 12px;
}
.settings-content[data-settings="about"] a:hover {
text-decoration: underline;
}
.settings-content .logo,
.settings-content .logo img {
display: block;
width: 55px;
height: 55px;
margin: 0 auto;
border-radius: 4px;
}
.settings-content .links {
text-align: center;
font-size: 14px;
margin-top: 10px;
}
.settings-content .social-links {
text-align: center;
/* margin-top: 10px; */
}
.settings-content .social-links a {
opacity: 0.7;
transition: opacity 0.1s ease-in-out;
}
.settings-content .social-links a,
.settings-content .social-links a:hover {
text-decoration: none;
margin: 0 10px;
}
.settings-content .social-links a:hover {
opacity: 1;
}
.settings-content .social-links svg {
width: 20px;
height: 20px;
}
.settings-content .about {
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
padding: 20px 40px;
max-width: 500px;
}
.about-container .about{
text-align: center;
}
.settings-content .version {
font-size: 9px;
color: #343c4f;
text-align: center;
margin-bottom: 10px;
opacity: 0.3;
transition: opacity 0.1s ease-in-out;
height: 12px;
}
.settings-content .version:hover {
opacity: 1;
}
.driver-usage {
background-color: white;
bottom: 0;
width: 100%;
box-sizing: border-box;
color: #3c4963;
height: 85px;
}
.credits {
padding: 0;
border: 1px solid #bfbfbf;
box-shadow: 1px 1px 10px 0px #8a8a8a;
width: 400px;
}
.credit-content a {
font-size: 15px;
}
.credits .credit-content {
padding: 20px;
}
.credit-content {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.credit-content ul {
max-height: 300px;
overflow-y: scroll;
background: #f4f4f4;
padding: 10px;
box-shadow: 2px 2px 5px 2px inset #CCC;
}
.credit-content li {
margin-bottom: 10px;
}
#storage-bar-wrapper {
width: 100%;
height: 15px;
border: 1px solid #8a9096;
border-radius: 3px;
background-color: #fff;
position: relative;
}
#storage-bar {
height: 15px;
background-color: #dbe3ef;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
width: 0;
}
#storage-used-percent {
position: absolute;
left: calc(50% - 20px);
text-align: center;
display: inline-block;
width: 40px;
font-size: 13px;
}
.usage-progbar-wrapper {
width: 100%;
height: 15px;
border: 1px solid #8a9096;
border-radius: 3px;
background-color: #fff;
position: relative;
}
.usage-progbar {
height: 15px;
background-color: #dbe3ef;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
width: 0;
}
.usage-progbar-percent {
position: absolute;
left: calc(50% - 20px);
text-align: center;
display: inline-block;
width: 40px;
font-size: 13px;
}
.version {
font-size: 9px;
color: #343c4f;
text-align: center;
margin-bottom: 10px;
opacity: 0.3;
transition: opacity 0.1s ease-in-out;
height: 12px;
}
.version:hover {
opacity: 1;
}
.language-list {
display: grid;
grid-template-columns: 33.333333333% 33.333333333% 33.333333333%;
}
.language-item {
cursor: pointer;
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
margin-right: 10px;
}
.language-item:hover {
background-color: #f6f6f6;
}
.language-item.active {
background-color: #e0e0e0;
}
.settings-card {
overflow: hidden;
padding: 10px 15px;
border: 1px solid;
border-radius: 4px;
background: #f7f7f7a1;
border: 1px solid #cccccc8f;
margin-bottom: 20px;
display: flex;
flex-direction: row;
align-items: center;
height: 45px;
}
.settings-card strong {
font-weight: 500;
}
.settings-card-danger {
border-color: #f0080866;
background: #ffecec;
color: rgb(215 2 2);
}
.error-message {
display: none;
color: rgb(215 2 2);
font-size: 14px;
margin-top: 10px;
margin-bottom: 10px;
padding: 10px;
border-radius: 4px;
border: 1px solid rgb(215 2 2);
text-align: center;
}
.account-deletion-confirmation-prompt {
text-align: center;
font-size: 16px;
padding: 20px;
font-weight: 400;
margin: -10px 10px 20px 10px;
-webkit-font-smoothing: antialiased;
color: #5f626d;
}
.account-deletion-confirmation-icon{
width: 70px;
margin: 20px auto 20px;
display: block;
margin-bottom: 20px;
}
.proceed-with-user-deletion{
margin-bottom: 20px;
}
.confirm-temporary-user-deletion{
width: 100%;
margin-bottom: 20px;
}
.confirm-user-deletion-password{
width: 100%;
margin-bottom: 20px;
}

View File

@@ -220,4 +220,7 @@ window.feature_flags = {
window.is_auto_arrange_enabled = true;
window.desktop_item_positions = {};
window.reset_item_positions = true; // The variable decides if the item positions should be reset when the user enabled auto arrange
window.reset_item_positions = true; // The variable decides if the item positions should be reset when the user enabled auto arrange
// default language
window.locale = 'en';

View File

@@ -722,7 +722,7 @@ window.mutate_user_preferences = function(user_preferences_delta) {
window.update_user_preferences = function(user_preferences) {
window.user_preferences = user_preferences;
localStorage.setItem('user_preferences', JSON.stringify(user_preferences));
window.locale = user_preferences.language;
window.locale = user_preferences.language ?? 'en';
}
window.sendWindowWillCloseMsg = function(iframe_element) {

View File

@@ -67,6 +67,9 @@ const update_username_in_gui = function(new_username){
else if (attr_item_path.startsWith('/' + window.user.username + '/'))
$el.attr('data-item-path', attr_item_path.replace('/' + window.user.username + '/', '/' + new_username + '/'));
}
// any element with username class
$('.username').text(new_username);
})
// todo update all window paths

View File

@@ -18,7 +18,7 @@
*/
import translations from './translations/translations.js';
window.ListSupportedLanguages = () => Object.keys(translations).map(lang => translations[lang]);
window.listSupportedLanguages = () => Object.keys(translations).map(lang => translations[lang]);
window.i18n = function (key, replacements = [], encode_html = true) {
if(typeof replacements === 'boolean' && encode_html === undefined){

View File

@@ -17,11 +17,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
function ChangeLanguage(lang) {
function changeLanguage(lang) {
window.locale = lang;
window.mutate_user_preferences({
language : lang,
});
}
export default ChangeLanguage;
export default changeLanguage;

View File

@@ -18,6 +18,7 @@
*/
const ar = {
name: "العربية",
english_name: "Arabic",
code: "ar",
dictionary: {
access_granted_to: "دخول مسموح به",

View File

@@ -18,6 +18,7 @@
*/
const bn = {
name: "বাংলা",
english_name: "Bengali",
code: "bn",
dictionary: {
access_granted_to: "অ্যাক্সেস মঞ্জুর করা হয়েছে",

View File

@@ -18,6 +18,7 @@
*/
const da = {
name: "Dansk",
english_name: "Danish",
code: "da",
dictionary: {
access_granted_to: "Adgang givet til",

View File

@@ -18,6 +18,7 @@
*/
const de = {
name: "Deutsch",
english_name: "German",
code: "de",
dictionary: {
access_granted_to: "Erlaubt zugriff auf",

View File

@@ -18,6 +18,7 @@
*/
const emoji = {
name: "🌍",
english_name: "Emoji",
code: "emoji",
dictionary: {
access_granted_to: "🔓✅",

View File

@@ -18,8 +18,11 @@
*/
const en = {
name: "English",
english_name: "English",
code: "en",
dictionary: {
about: "About",
account: "Account",
access_granted_to: "Access Granted To",
add_existing_account: "Add Existing Account",
all_fields_required: 'All fields are required.',
@@ -43,6 +46,7 @@ const en = {
confirm_delete_single_item: 'Do you want to permanently delete this item?',
confirm_open_apps_log_out: 'You have open apps. Are you sure you want to log out?',
confirm_new_password: "Confirm New Password",
confirm_delete_user: "Are you sure you want to delete your account? All your files and data will be permanently deleted. This action cannot be undone.",
contact_us: "Contact Us",
contain: 'Contain',
continue: "Continue",
@@ -53,14 +57,17 @@ const en = {
create_account: "Create Account",
create_free_account: "Create Free Account",
create_shortcut: "Create Shortcut",
credits: "Credits",
current_password: "Current Password",
cut: 'Cut',
date_modified: 'Date modified',
delete: 'Delete',
delete_account: "Delete Account",
delete_permanently: "Delete Permanently",
deploy_as_app: 'Deploy as app',
descending: 'Descending',
desktop_background_fit: "Fit",
developers: "Developers",
dir_published_as_website: `%strong% has been published to:`,
disassociate_dir: "Disassociate Directory",
download: 'Download',
@@ -71,6 +78,7 @@ const en = {
empty_trash: 'Empty Trash',
empty_trash_confirmation: `Are you sure you want to permanently delete the items in Trash?`,
emptying_trash: 'Emptying Trash…',
enter_password_to_confirm_delete_user: "Enter your password to confirm account deletion",
feedback: "Feedback",
feedback_c2a: "Please use the form below to send us your feedback, comments, and bug reports.",
feedback_sent_confirmation: "Thank you for contacting us. If you have an email associated with your account, you will hear back from us as soon as possible.",
@@ -87,6 +95,8 @@ const en = {
items_in_trash_cannot_be_renamed: `This item can't be renamed because it's in the trash. To rename this item, first drag it out of the Trash.`,
jpeg_image: 'JPEG image',
keep_in_taskbar: 'Keep in Taskbar',
language: "Language",
license: "License",
loading: 'Loading',
log_in: "Log In",
log_into_another_account_anyway: 'Log into another account anyway',
@@ -113,6 +123,7 @@ const en = {
open_in_new_tab: "Open in New Tab",
open_in_new_window: "Open in New Window",
open_with: "Open With",
oss_code_and_content: "Open Source Software and Content",
password: "Password",
password_changed: "Password changed.",
passwords_do_not_match: '`New Password` and `Confirm New Password` do not match.',
@@ -120,14 +131,18 @@ const en = {
paste_into_folder: "Paste Into Folder",
pick_name_for_website: "Pick a name for your website:",
picture: "Picture",
plural_suffix: 's',
powered_by_puter_js: `Powered by <a href="https://docs.puter.com/" target="_blank">Puter.js</a>`,
preparing: "Preparing...",
preparing_for_upload: "Preparing for upload...",
privacy: "Privacy",
proceed_to_login: 'Proceed to login',
proceed_with_account_deletion: "Proceed with Account Deletion",
properties: "Properties",
publish: "Publish",
publish_as_website: 'Publish as website',
plural_suffix: 's',
puter_description: `Puter is a privacy-first personal cloud to keep all your files, apps, and games in one
secure place, accessible from anywhere at any time.`,
recent: "Recent",
recover_password: "Recover Password",
refer_friends_c2a: "Get 1 GB for every friend who creates and confirms an account on Puter. Your friend will get 1 GB too!",
@@ -153,6 +168,7 @@ const en = {
send: "Send",
send_password_recovery_email: "Send Password Recovery Email",
session_saved: "Thank you for creating an account. This session has been saved.",
settings: "Settings",
set_new_password: "Set New Password",
share_to: "Share to",
show_all_windows: "Show All Windows",
@@ -164,15 +180,21 @@ const en = {
skip: 'Skip',
sort_by: 'Sort by',
start: 'Start',
status: "Status",
storage_usage: "Storage Usage",
taking_longer_than_usual: 'Taking a little longer than usual. Please wait...',
terms: "Terms",
text_document: 'Text document',
tos_fineprint: `By clicking 'Create Free Account' you agree to Puter's <a href="https://puter.com/terms" target="_blank">Terms of Service</a> and <a href="https://puter.com/privacy" target="_blank">Privacy Policy</a>.`,
trash: 'Trash',
type: 'Type',
type_confirm_to_delete_account: "Type 'confirm' to delete your account.",
undo: 'Undo',
unlimited: 'Unlimited',
unzip: "Unzip",
upload: 'Upload',
upload_here: 'Upload here',
usage: 'Usage',
username: "Username",
username_changed: 'Username updated successfully.',
versions: "Versions",

View File

@@ -18,6 +18,7 @@
*/
const es = {
name: "Español",
english_name: "Spanish",
code: "es",
dictionary: {
access_granted_to: "Acceso Permitido A",

View File

@@ -18,6 +18,7 @@
*/
const fa = {
name: "فارسی",
english_name: "Farsi",
code: "fa",
dictionary: {
access_granted_to: "دسترسی داده شده به",

View File

@@ -18,6 +18,7 @@
*/
const fi = {
name: "Suomi",
english_name: "Finnish",
code: "fi",
dictionary: {
access_granted_to: "Käyttöoikeus Myönnetty",

View File

@@ -18,6 +18,7 @@
*/
const fr = {
name: "Français",
english_name: "French",
code: "fr",
dictionary: {
access_granted_to: "Accès accordé à",

View File

@@ -18,6 +18,7 @@
*/
const hy = {
name: "Հայերեն",
english_name: "Armenian",
code: "hy",
dictionary: {
access_granted_to: "Մուտքը տրված է՝",

View File

@@ -18,6 +18,7 @@
*/
const it = {
name: "Italiano",
english_name: "Italian",
code: "it",
dictionary: {
access_granted_to: "Accesso garantito a",

View File

@@ -18,6 +18,7 @@
*/
const ko = {
name: "한국어",
english_name: "Korean",
code: "ko",
dictionary: {
access_granted_to: "접근 권한 부여",

View File

@@ -18,6 +18,7 @@
*/
const nb = {
name: "Norsk Bokmål",
english_name: "Norwegian Bokmål",
code: "nb",
dictionary: {
access_granted_to: "Tilgang gitt til",

View File

@@ -18,6 +18,7 @@
*/
const nn = {
name: "Norsk Nynorsk",
english_name: "Norwegian Nynorsk",
code: "nn",
dictionary: {
access_granted_to: "Tilgang gjeven til",

View File

@@ -18,6 +18,7 @@
*/
const ro = {
name: "Română",
english_name: "Romanian",
code: "ro",
dictionary: {
access_granted_to: "Acces acordat pentru",

View File

@@ -18,6 +18,7 @@
*/
const sv = {
name: "Svenska",
english_name: "Swedish",
code: "sv",
dictionary: {
access_granted_to: "Tillgång beviljad till",

View File

@@ -18,6 +18,7 @@
*/
const th = {
name: "ไทย",
english_name: "Thai",
code: "th",
dictionary: {
access_granted_to: "อนุญาตให้เข้าถึง",

View File

@@ -18,6 +18,7 @@
*/
const ur = {
name: "اردو",
english_name: "Urdu",
code: "ur",
dictionary: {
access_granted_to: "رسائی مسموح ہے",

View File

@@ -18,6 +18,7 @@
*/
const zh = {
name: "中文",
english_name: "Chinese",
code: "zh",
dictionary: {
access_granted_to: "访问授权给",

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="59px" height="59px" stroke-width="1.7" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#000000"><path d="M21 7.35304L21 16.647C21 16.8649 20.8819 17.0656 20.6914 17.1715L12.2914 21.8381C12.1102 21.9388 11.8898 21.9388 11.7086 21.8381L3.30861 17.1715C3.11814 17.0656 3 16.8649 3 16.647L2.99998 7.35304C2.99998 7.13514 3.11812 6.93437 3.3086 6.82855L11.7086 2.16188C11.8898 2.06121 12.1102 2.06121 12.2914 2.16188L20.6914 6.82855C20.8818 6.93437 21 7.13514 21 7.35304Z" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3.52844 7.29357L11.7086 11.8381C11.8898 11.9388 12.1102 11.9388 12.2914 11.8381L20.5 7.27777" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 21L12 12" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path></svg>

After

Width:  |  Height:  |  Size: 947 B

144
src/icons/danger.svg Normal file
View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
id="svg3468"
height="48"
width="48"
version="1.1">
<defs
id="defs3470">
<linearGradient
id="linearGradient4011">
<stop
offset="0"
style="stop-color:#ffffff;stop-opacity:1"
id="stop4013" />
<stop
offset="0.507761"
style="stop-color:#ffffff;stop-opacity:0.23529412"
id="stop4015" />
<stop
offset="0.83456558"
style="stop-color:#ffffff;stop-opacity:0.15686275"
id="stop4017" />
<stop
offset="1"
style="stop-color:#ffffff;stop-opacity:0.39215687"
id="stop4019" />
</linearGradient>
<linearGradient
id="linearGradient947">
<stop
offset="0"
style="stop-color:#ed5353;stop-opacity:1"
id="stop943" />
<stop
offset="1"
style="stop-color:#c6262e;stop-opacity:1"
id="stop945" />
</linearGradient>
<linearGradient
x1="2035.1652"
y1="3208.0737"
x2="2035.1652"
y2="3241.9966"
id="linearGradient11527-6-5-3"
xlink:href="#linearGradient947"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.5712555,0,0,1.1746645,-3179.5055,-3763.7759)" />
<linearGradient
x1="71.204407"
y1="6.2375584"
x2="71.204407"
y2="44.340794"
id="linearGradient3019"
xlink:href="#linearGradient4011"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.054054,0,0,1.054054,-51.611001,-2.727901)" />
<radialGradient
xlink:href="#linearGradient3820-7-2"
id="radialGradient3300-8"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.3823536,7.5555777e-8,-1.8372241e-8,0.1115198,-5.9254271,36.335849)"
cx="99.189415"
cy="185.29727"
fx="99.189415"
fy="185.29727"
r="62.769119" />
<linearGradient
id="linearGradient3820-7-2">
<stop
id="stop3822-2-6"
style="stop-color:#000000;stop-opacity:1"
offset="0" />
<stop
id="stop3824-1-2"
style="stop-color:#000000;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient3820-7-2"
id="radialGradient4192-6"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.2549024,5.3967361e-8,-1.2248161e-8,0.07965545,6.7163816,44.240666)"
cx="99.189415"
cy="185.29727"
fx="99.189415"
fy="185.29727"
r="62.769119" />
</defs>
<metadata
id="metadata3473">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g4198-4"
transform="matrix(0.70833333,0,0,0.71428273,1.33333,0.78460484)"
style="stroke-width:1.40587306">
<path
d="m 55.999998,57.0017 a 24,6.9985714 0 1 1 -47.9999991,0 24,6.9985714 0 1 1 47.9999991,0 z"
id="path3818-0-6"
style="opacity:0.2;fill:url(#radialGradient3300-8);fill-opacity:1;stroke:none;stroke-width:1.40587306" />
<path
style="opacity:0.4;fill:url(#radialGradient4192-6);fill-opacity:1;stroke:none;stroke-width:1.40587306"
id="path4190-2"
d="m 47.999998,59.0017 a 16,4.9988822 0 1 1 -31.999999,0 16,4.9988822 0 1 1 31.999999,0 z" />
</g>
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.98999999;fill:url(#linearGradient11527-6-5-3);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999988;marker:none;enable-background:accumulate"
id="path2555-7-8-5-0-9"
d="m 24,3.4999989 c -11.311197,0 -20.5000003,9.1888021 -20.5000003,20.5000001 0,11.311197 9.1888033,20.5 20.5000003,20.5 11.311197,0 20.500019,-9.188803 20.5,-20.5 C 44.5,12.688801 35.311197,3.4999989 24,3.4999989 Z" />
<path
d="m 43.5,23.999308 c 0,10.7699 -8.73109,19.50069 -19.499753,19.50069 -10.769649,0 -19.5002474,-8.730889 -19.5002474,-19.50069 0,-10.769402 8.7305984,-19.4993079 19.5002474,-19.4993079 C 34.76891,4.5000001 43.5,13.229906 43.5,23.999308 l 0,0 z"
id="path8655"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.4;fill:none;stroke:url(#linearGradient3019);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<path
d="M 24.000003,3.4999979 C 12.68881,3.4999979 3.4999996,12.688804 3.4999996,23.999998 3.4999996,35.311192 12.68881,44.500003 24.000003,44.5 35.311191,44.5 44.500011,35.311192 44.5,23.999998 44.5,12.688804 35.311191,3.4999979 24.000003,3.4999979 Z"
id="path2555-6"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.5;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#7a0000;stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="opacity:0.05;fill:#7a0000;fill-opacity:1;stroke:none;stroke-width:0.83747208;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
id="path4159-7-3"
d="m 24,10.855455 c -2.197926,0 -4,1.802074 -4,4 0,3.238442 1.017769,7.210006 2.019665,15.225174 a 0.98016537,0.98016537 0 0 0 0.973452,0.857424 l 2.013766,0 a 0.98016537,0.98016537 0 0 0 0.973452,-0.857424 C 26.982231,22.065461 28,18.093897 28,14.855455 c 0,-2.197926 -1.802074,-4 -4,-4 z M 24,32 c -2.197926,0 -4,1.802074 -4,4 0,2.197926 1.802074,4 4,4 2.197926,0 4,-1.802074 4,-4 0,-2.197926 -1.802074,-4 -4,-4 z" />
<path
style="opacity:0.15;fill:#7a0000;fill-opacity:1;stroke:none;stroke-width:0.84323651;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
d="m 24,12 c -1.656854,0 -3,1.343147 -3,3 0,3 1,7 2,15 l 2,0 c 1,-8 2,-12 2,-14.999999 0,-1.656854 -1.343146,-3 -3,-3 z m 0,21 c -1.656854,0 -3,1.343146 -3,3 0,1.656854 1.343146,3 3,3 1.656854,0 3,-1.343146 3,-3 0,-1.656854 -1.343146,-3 -3,-3 z"
id="path4159-7" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.49803922"
d="m 24,11 c -1.656854,0 -3,1.343147 -3,3 0,3 1,7 2,15 l 2,0 C 26,21 27,17 27,14.000001 27,12.343147 25.656854,11 24,11 Z m 0,21 c -1.656854,0 -3,1.343146 -3,3 0,1.656854 1.343146,3 3,3 1.656854,0 3,-1.343146 3,-3 0,-1.656854 -1.343146,-3 -3,-3 z"
id="path4159" />
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

1
src/icons/language.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="59px" height="59px" stroke-width="1.4" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#000000"><path d="M2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12Z" stroke="#000000" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M13 2.04932C13 2.04932 16 5.99994 16 11.9999C16 17.9999 13 21.9506 13 21.9506" stroke="#000000" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11 21.9506C11 21.9506 8 17.9999 8 11.9999C8 5.99994 11 2.04932 11 2.04932" stroke="#000000" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M2.62964 15.5H21.3704" stroke="#000000" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M2.62964 8.5H21.3704" stroke="#000000" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"></path></svg>

After

Width:  |  Height:  |  Size: 987 B

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<svg x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<g stroke-width="3" transform="matrix(0, 1, -1, 0, 47.999504, 0.000014)">
<polyline points="39 24 25 24 25 28" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="3" stroke-linejoin="miter"/>
<polyline points="35.879 10.121 32 14 25 14 25 18" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="3" stroke-linejoin="miter"/>
<path d="M13,26a10.29,10.29,0,0,1-7.2-3" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="3" stroke-linejoin="miter"/>
<path d="M17,31.6A5.826,5.826,0,0,1,13,26a5.731,5.731,0,0,1,2-4.4" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="3" stroke-linejoin="miter"/>
<path d="M35.879,37.879,32,34H25v2A9.9,9.9,0,0,1,15,46,9.9,9.9,0,0,1,5,36a9.058,9.058,0,0,1,.6-3.2A5.627,5.627,0,0,1,3,28a5.888,5.888,0,0,1,2.8-5A9.994,9.994,0,0,1,3,16,9.9,9.9,0,0,1,13,6h.4A5.826,5.826,0,0,1,19,2a5.893,5.893,0,0,1,6,6" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="3" stroke-linejoin="miter"/>
<circle cx="38" cy="8" r="3" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="3" data-color="color-2" stroke-linejoin="miter"/>
<circle cx="42" cy="24" r="3" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="3" data-color="color-2" stroke-linejoin="miter"/>
<circle cx="38" cy="40" r="3" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="3" data-color="color-2" stroke-linejoin="miter"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-palette-fill" viewBox="0 0 16 16">
<path d="M12.433 10.07C14.133 10.585 16 11.15 16 8a8 8 0 1 0-8 8c1.996 0 1.826-1.504 1.649-3.08-.124-1.101-.252-2.237.351-2.92.465-.527 1.42-.237 2.433.07M8 5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3m4.5 3a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3M5 6.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m.5 6.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3"/>
</svg>

After

Width:  |  Height:  |  Size: 464 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="59px" height="59px" stroke-width="1.7" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#000000"><path d="M20.5096 9.54C20.4243 9.77932 20.2918 9.99909 20.12 10.1863C19.9483 10.3735 19.7407 10.5244 19.5096 10.63C18.2796 11.1806 17.2346 12.0745 16.5002 13.2045C15.7659 14.3345 15.3733 15.6524 15.3696 17C15.3711 17.4701 15.418 17.9389 15.5096 18.4C15.5707 18.6818 15.5747 18.973 15.5215 19.2564C15.4682 19.5397 15.3588 19.8096 15.1996 20.05C15.0649 20.2604 14.8877 20.4403 14.6793 20.5781C14.4709 20.7158 14.2359 20.8085 13.9896 20.85C13.4554 20.9504 12.9131 21.0006 12.3696 21C11.1638 21.0006 9.97011 20.7588 8.85952 20.2891C7.74893 19.8194 6.74405 19.1314 5.90455 18.2657C5.06506 17.4001 4.40807 16.3747 3.97261 15.2502C3.53714 14.1257 3.33208 12.9252 3.36959 11.72C3.4472 9.47279 4.3586 7.33495 5.92622 5.72296C7.49385 4.11097 9.60542 3.14028 11.8496 3H12.3596C14.0353 3.00042 15.6777 3.46869 17.1017 4.35207C18.5257 5.23544 19.6748 6.49885 20.4196 8C20.6488 8.47498 20.6812 9.02129 20.5096 9.52V9.54Z" stroke="#000000" stroke-width="1.7"></path><path d="M8 16.01L8.01 15.9989" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path><path d="M6 12.01L6.01 11.9989" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8 8.01L8.01 7.99889" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 6.01L12.01 5.99889" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path><path d="M16 8.01L16.01 7.99889" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-speedometer2" viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5V6a.5.5 0 0 1-1 0V4.5A.5.5 0 0 1 8 4M3.732 5.732a.5.5 0 0 1 .707 0l.915.914a.5.5 0 1 1-.708.708l-.914-.915a.5.5 0 0 1 0-.707M2 10a.5.5 0 0 1 .5-.5h1.586a.5.5 0 0 1 0 1H2.5A.5.5 0 0 1 2 10m9.5 0a.5.5 0 0 1 .5-.5h1.5a.5.5 0 0 1 0 1H12a.5.5 0 0 1-.5-.5m.754-4.246a.39.39 0 0 0-.527-.02L7.547 9.31a.91.91 0 1 0 1.302 1.258l3.434-4.297a.39.39 0 0 0-.029-.518z"/>
<path fill-rule="evenodd" d="M0 10a8 8 0 1 1 15.547 2.661c-.442 1.253-1.845 1.602-2.932 1.25C11.309 13.488 9.475 13 8 13c-1.474 0-3.31.488-4.615.911-1.087.352-2.49.003-2.932-1.25A8 8 0 0 1 0 10m8-7a7 7 0 0 0-6.603 9.329c.203.575.923.876 1.68.63C4.397 12.533 6.358 12 8 12s3.604.532 4.923.96c.757.245 1.477-.056 1.68-.631A7 7 0 0 0 8 3"/>
</svg>

After

Width:  |  Height:  |  Size: 866 B

1
src/icons/user.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="59px" height="59px" stroke-width="1.7" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#000000"><path d="M5 20V19C5 15.134 8.13401 12 12 12V12C15.866 12 19 15.134 19 19V20" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 12C14.2091 12 16 10.2091 16 8C16 5.79086 14.2091 4 12 4C9.79086 4 8 5.79086 8 8C8 10.2091 9.79086 12 12 12Z" stroke="#000000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"></path></svg>

After

Width:  |  Height:  |  Size: 557 B

BIN
src/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1870,7 +1870,7 @@ window.initgui = async function(){
*/
$(document).on("logout", async function(event) {
// is temp user?
if(window.user && window.user.is_temp){
if(window.user && window.user.is_temp && !window.user.deleted){
const alert_resp = await UIAlert({
message: `<strong>Save account before logging out!</strong><p>You are using a temporary account and logging out will erase all your data.</p>`,
buttons:[