mirror of
https://github.com/HeyPuter/puter.git
synced 2025-01-24 06:50:22 +08:00
feat: Implement profile pictures
This commit is contained in:
parent
59fa600f2b
commit
0885937f03
@ -14,7 +14,8 @@
|
||||
"/lib/timeago.min.js",
|
||||
"/lib/iro.min.js",
|
||||
"/lib/isMobile.min.js",
|
||||
"/lib/fflate-0.8.2.min.js"
|
||||
"/lib/fflate-0.8.2.min.js",
|
||||
"/lib/croppie.min.js"
|
||||
],
|
||||
"css_paths": [
|
||||
"/css/normalize.css",
|
||||
|
@ -22,6 +22,7 @@ import UIWindowChangeEmail from './UIWindowChangeEmail.js';
|
||||
import UIWindowChangeUsername from '../UIWindowChangeUsername.js';
|
||||
import UIWindowConfirmUserDeletion from './UIWindowConfirmUserDeletion.js';
|
||||
import UIWindowManageSessions from '../UIWindowManageSessions.js';
|
||||
import UIWindow from '../UIWindow.js';
|
||||
|
||||
// About
|
||||
export default {
|
||||
@ -29,7 +30,14 @@ export default {
|
||||
title_i18n_key: 'account',
|
||||
icon: 'user.svg',
|
||||
html: () => {
|
||||
let h = `<h1>${i18n('account')}</h1>`;
|
||||
let h = '';
|
||||
// h += `<h1>${i18n('account')}</h1>`;
|
||||
|
||||
// profile picture
|
||||
h += `<div style="overflow: hidden; display: flex; margin-bottom: 20px; flex-direction: column; align-items: center;">`;
|
||||
h += `<div class="profile-picture change-profile-picture" style="background-image: url('${html_encode(window.user?.profile?.picture ?? window.icons['profile.svg'])}');">`;
|
||||
h += `</div>`;
|
||||
h += `</div>`;
|
||||
|
||||
// change password button
|
||||
if(!window.user.is_temp){
|
||||
@ -125,5 +133,49 @@ export default {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$el_window.find('.change-profile-picture').on('click', async function (e) {
|
||||
// open dialog
|
||||
UIWindow({
|
||||
path: '/' + window.user.username + '/Desktop',
|
||||
// this is the uuid of the window to which this dialog will return
|
||||
parent_uuid: $el_window.attr('data-element_uuid'),
|
||||
allowed_file_types: ['.png', '.jpg', '.jpeg'],
|
||||
show_maximize_button: false,
|
||||
show_minimize_button: false,
|
||||
title: 'Open',
|
||||
is_dir: true,
|
||||
is_openFileDialog: true,
|
||||
selectable_body: false,
|
||||
});
|
||||
})
|
||||
|
||||
$el_window.on('file_opened', async function(e){
|
||||
let selected_file = Array.isArray(e.detail) ? e.detail[0] : e.detail;
|
||||
// set profile picture
|
||||
const profile_pic = await puter.fs.read(selected_file.path)
|
||||
// blob to base64
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(profile_pic);
|
||||
reader.onloadend = function() {
|
||||
// resizes the image to 150x150
|
||||
const img = new Image();
|
||||
img.src = reader.result;
|
||||
img.onload = function() {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
canvas.width = 150;
|
||||
canvas.height = 150;
|
||||
ctx.drawImage(img, 0, 0, 150, 150);
|
||||
const base64data = canvas.toDataURL('image/png');
|
||||
// update profile picture
|
||||
$el_window.find('.profile-picture').css('background-image', 'url(' + html_encode(base64data) + ')');
|
||||
$('.profile-image').css('background-image', 'url(' + html_encode(base64data) + ')');
|
||||
$('.profile-image').addClass('profile-image-has-picture');
|
||||
// update profile picture
|
||||
update_profile(window.user.username, {picture: base64data})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
};
|
||||
|
@ -1052,8 +1052,8 @@ async function UIDesktop(options){
|
||||
ht += `<div class="toolbar-btn search-btn" title="Search" style="background-image:url('${window.icons['search.svg']}')"></div>`;
|
||||
|
||||
// user options menu
|
||||
ht += `<div class="toolbar-btn user-options-menu-btn" style="background-image:url(${window.icons['profile.svg']})">`;
|
||||
h += `<span class="user-options-menu-username">${window.user.username}</span>`;
|
||||
ht += `<div class="toolbar-btn user-options-menu-btn profile-pic" style="display:block;">`;
|
||||
ht += `<div class="profile-image ${window.user?.profile?.picture && 'profile-image-has-picture'}" style="border-radius: 50%; background-image:url(${window.user?.profile?.picture || window.icons['profile.svg']}); box-sizing: border-box; width: 17px !important; height: 17px !important; background-size: contain; background-repeat: no-repeat; background-position: center; background-position: center; background-size: cover;"></div>`;
|
||||
ht += `</div>`;
|
||||
ht += `</div>`;
|
||||
|
||||
|
@ -32,10 +32,14 @@ async function UIWindowSessionList(options){
|
||||
h += `<div class="loading">${i18n('signing_in')}</div>`;
|
||||
// session list
|
||||
h += `<div class="hide-scrollbar" style="overflow-y: scroll; max-width: 400px; margin: 0 auto;">`;
|
||||
h += `<h1 style="text-align: center; font-size: 18px; font-weight: normal; color: #757575;"><img src="${window.icons['logo-white.svg']}" style="padding: 4px; background-color: blue; border-radius: 5px; width: 25px; box-sizing: border-box; margin-bottom: -6px; margin-right: 6px;">${i18n('sign_in_with_puter')}</h1>`
|
||||
h += `<h1 style="text-align: center; font-size: 18px; font-weight: normal; color: #757575; margin-bottom: 30px;"><img src="${window.icons['logo-white.svg']}" style="padding: 4px; background-color: blue; border-radius: 5px; width: 25px; box-sizing: border-box; margin-bottom: -6px; margin-right: 6px;">${i18n('sign_in_with_puter')}</h1>`
|
||||
for (let index = 0; index < window.logged_in_users.length; index++) {
|
||||
const l_user = window.logged_in_users[index];
|
||||
h += `<div data-uuid="${l_user.uuid}" class="session-entry">${l_user.username}</div>`;
|
||||
h += `<div data-uuid="${l_user.uuid}" class="session-entry" style="display: flex; padding: 15px 10px;">`;
|
||||
// profile picture
|
||||
h += `<div class="profile-picture" style="background-color: #cbced1; width: 30px; height: 30px; margin:0; margin-right: 10px; background-image: url('${l_user.profile.picture ?? window.icons['profile.svg']}');"></div>`;
|
||||
h += `<div style="display: flex; align-items: center;">${l_user.username}</div>`;
|
||||
h += `</div>`;
|
||||
}
|
||||
h += `</div>`;
|
||||
// c2a
|
||||
|
@ -79,7 +79,7 @@
|
||||
font-variation-settings: "slnt"0;
|
||||
}
|
||||
|
||||
pre{
|
||||
pre {
|
||||
font-family: "Inter", "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
@ -611,7 +611,7 @@ span.header-sort-icon img {
|
||||
}
|
||||
|
||||
.item-name, .item-name-editor, .item-name-shadow {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
text-shadow: 0px 0px 3px #00000082, 0px 0px 3px #00000082, 0px 0px 3px #00000082;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
@ -1263,27 +1263,32 @@ span.header-sort-icon img {
|
||||
background-color: #fefeff;
|
||||
}
|
||||
|
||||
.window-sidebar-item-placeholder{
|
||||
.window-sidebar-item-placeholder {
|
||||
height: 27px !important;
|
||||
}
|
||||
|
||||
.window-sidebar-item {
|
||||
cursor: pointer !important;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.window-sidebar-item:not(.window-sidebar-title):hover {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.window-sidebar-item.grabbing {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
|
||||
.window-sidebar-item-dragging {
|
||||
background-color: #f5f5f5 !important;
|
||||
opacity: 0.8;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.ui-sortable-helper {
|
||||
background: white !important;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.window-sidebar-item-icon {
|
||||
@ -1362,16 +1367,17 @@ span.header-sort-icon img {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.window-filedialog-upload-here{
|
||||
.window-filedialog-upload-here {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
opacity: 0.7;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.window-filedialog-upload-here:hover{
|
||||
.window-filedialog-upload-here:hover {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.savefiledialog-save-btn, .openfiledialog-open-btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
@ -1590,7 +1596,8 @@ span.header-sort-icon img {
|
||||
border-bottom: none;
|
||||
border-top: 1px solid #00000033;
|
||||
}
|
||||
.context-menu .context-menu-divider{
|
||||
|
||||
.context-menu .context-menu-divider {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
@ -2115,11 +2122,11 @@ label {
|
||||
background-color: #9dacbd;
|
||||
}
|
||||
|
||||
.permission-editor-badge{
|
||||
.permission-editor-badge {
|
||||
background-color: #007cff;
|
||||
}
|
||||
|
||||
.permission-viewer-badge{
|
||||
.permission-viewer-badge {
|
||||
background-color: #41c95d;
|
||||
}
|
||||
|
||||
@ -2168,20 +2175,24 @@ label {
|
||||
max-height: 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ui-menu{
|
||||
|
||||
.ui-menu {
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.ui-menu .ui-menu-item{
|
||||
|
||||
.ui-menu .ui-menu-item {
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item .ui-menu-item-wrapper {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item:hover .ui-menu-item-wrapper,
|
||||
.ui-menu .ui-menu-item:focus .ui-menu-item-wrapper,
|
||||
.ui-menu .ui-menu-item:active .ui-menu-item-wrapper,
|
||||
@ -2856,13 +2867,14 @@ fieldset[name=number-code] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.welcome-window-close-button{
|
||||
.welcome-window-close-button {
|
||||
opacity: 0.7;
|
||||
font-weight: 300;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
.welcome-window-close-button:hover{
|
||||
|
||||
.welcome-window-close-button:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@ -3690,12 +3702,12 @@ fieldset[name=number-code] {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.settings-sidebar-title{
|
||||
margin-bottom: 20px;
|
||||
font-weight: bold;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
margin-top: 15px;
|
||||
color: #8c8c8c;
|
||||
.settings-sidebar-title {
|
||||
margin-bottom: 20px;
|
||||
font-weight: bold;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
margin-top: 15px;
|
||||
color: #8c8c8c;
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
@ -3835,6 +3847,30 @@ fieldset[name=number-code] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.profile-picture {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
border: 1px solid #EEE;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
margin-right: 0;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
background-color: #c5cdd4;
|
||||
}
|
||||
|
||||
.profile-picture:hover {
|
||||
background-color: #a6afb7;
|
||||
}
|
||||
|
||||
.profile-image-has-picture{
|
||||
border: 1px solid white;
|
||||
}
|
||||
.driver-usage {
|
||||
background-color: white;
|
||||
bottom: 0;
|
||||
@ -3982,7 +4018,7 @@ fieldset[name=number-code] {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.language-item .checkmark{
|
||||
.language-item .checkmark {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
@ -3991,9 +4027,11 @@ fieldset[name=number-code] {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.language-item.active {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.language-item.active .checkmark {
|
||||
display: inline-block;
|
||||
}
|
||||
@ -4011,9 +4049,11 @@ fieldset[name=number-code] {
|
||||
align-items: center;
|
||||
height: 45px;
|
||||
}
|
||||
.settings-card .button{
|
||||
|
||||
.settings-card .button {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.thin-card {
|
||||
padding: 0 15px;
|
||||
}
|
||||
@ -4193,6 +4233,7 @@ fieldset[name=number-code] {
|
||||
.visible-xs {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.settings-sidebar {
|
||||
display: none;
|
||||
position: fixed;
|
||||
@ -4205,6 +4246,7 @@ fieldset[name=number-code] {
|
||||
.visible-sm {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.settings-sidebar {
|
||||
display: none;
|
||||
position: fixed;
|
||||
@ -4231,46 +4273,49 @@ fieldset[name=number-code] {
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-toggle{
|
||||
position: absolute;
|
||||
.sidebar-toggle {
|
||||
position: absolute;
|
||||
z-index: 9999999999;
|
||||
left: 2px;
|
||||
border: 0;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
border: 0;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.sidebar-toggle .sidebar-toggle-button {
|
||||
height: 20px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.sidebar-toggle span:nth-child(1) {
|
||||
margin-top: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.sidebar-toggle span {
|
||||
border-bottom: 2px solid #858585;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
width: 100%;
|
||||
border-bottom: 2px solid #858585;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings-sidebar.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.welcome-window-footer{
|
||||
position: absolute; bottom: 20px;
|
||||
.welcome-window-footer {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
.welcome-window-footer a{
|
||||
.welcome-window-footer a {
|
||||
color: #727c8d;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.welcome-window-footer a:hover{
|
||||
.welcome-window-footer a:hover {
|
||||
color: #1d1e23;
|
||||
}
|
||||
|
||||
@ -4279,7 +4324,7 @@ fieldset[name=number-code] {
|
||||
* Search
|
||||
* ------------------------------------
|
||||
*/
|
||||
.search-input-wrapper{
|
||||
.search-input-wrapper {
|
||||
width: 100%;
|
||||
border-radius: 5px;
|
||||
padding-bottom: 10px;
|
||||
@ -4290,13 +4335,15 @@ fieldset[name=number-code] {
|
||||
box-sizing: border-box;
|
||||
background: #f1f6fc;
|
||||
}
|
||||
.search-input{
|
||||
|
||||
.search-input {
|
||||
padding-left: 33px !important;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 5px center;
|
||||
background-size: 20px;
|
||||
}
|
||||
.search-results{
|
||||
|
||||
.search-results {
|
||||
padding-right: 15px;
|
||||
margin-top: 70px;
|
||||
padding-left: 15px;
|
||||
@ -4304,18 +4351,21 @@ fieldset[name=number-code] {
|
||||
padding-bottom: 5px;
|
||||
display: none;
|
||||
}
|
||||
.search-result{
|
||||
padding: 10px; cursor: pointer;
|
||||
|
||||
.search-result {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.search-result-active{
|
||||
|
||||
.search-result-active {
|
||||
background-color: #4092da;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.search-results .search-result:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
}
|
@ -445,6 +445,35 @@ window.update_auth_data = async (auth_token, user)=>{
|
||||
$('.user-email').html(html_encode(user.email));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// get .profile file and update user profile
|
||||
// ----------------------------------------------------
|
||||
user.profile = {};
|
||||
puter.fs.read('/'+user.username+'/Public/.profile').then((blob)=>{
|
||||
blob.text()
|
||||
.then(text => {
|
||||
const profile = JSON.parse(text);
|
||||
if(profile.picture){
|
||||
window.user.profile.picture = html_encode(profile.picture);
|
||||
}
|
||||
|
||||
// update profile picture in GUI
|
||||
if(window.user.profile.picture){
|
||||
$('.profile-pic').css('background-image', 'url('+window.user.profile.picture+')');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error converting Blob to JSON:', error);
|
||||
});
|
||||
}).catch((e)=>{
|
||||
if(e?.code === "subject_does_not_exist"){
|
||||
// create .profile file
|
||||
puter.fs.write('/'+user.username+'/Public/.profile', JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
const to_storable_user = user => {
|
||||
const storable_user = {...user};
|
||||
delete storable_user.taskbar_items;
|
||||
@ -2597,3 +2626,29 @@ window.detectHostOS = function(){
|
||||
}
|
||||
}
|
||||
|
||||
window.update_profile = function(username, key_vals){
|
||||
puter.fs.read('/'+username+'/Public/.profile').then((blob)=>{
|
||||
blob.text()
|
||||
.then(text => {
|
||||
const profile = JSON.parse(text);
|
||||
|
||||
for (const key in key_vals) {
|
||||
profile[key] = key_vals[key];
|
||||
// update window.user.profile
|
||||
window.user.profile[key] = key_vals[key];
|
||||
}
|
||||
|
||||
puter.fs.write('/'+username+'/Public/.profile', JSON.stringify(profile));
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error converting Blob to JSON:', error);
|
||||
});
|
||||
}).catch((e)=>{
|
||||
if(e?.code === "subject_does_not_exist"){
|
||||
// create .profile file
|
||||
puter.fs.write('/'+username+'/Public/.profile', JSON.stringify({}));
|
||||
}
|
||||
// Ignored
|
||||
console.log(e);
|
||||
});
|
||||
}
|
1
src/gui/src/lib/croppie.min.js
vendored
Normal file
1
src/gui/src/lib/croppie.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -32,6 +32,7 @@ const lib_paths =[
|
||||
`/lib/iro.min.js`,
|
||||
`/lib/isMobile.min.js`,
|
||||
`/lib/fflate-0.8.2.min.js`,
|
||||
`/lib/croppie.min.js`
|
||||
]
|
||||
|
||||
// Ordered list of CSS stylesheets
|
||||
|
@ -11,5 +11,6 @@ module.exports = [
|
||||
"timeago.min.js",
|
||||
"iro.min.js",
|
||||
"isMobile.min.js",
|
||||
"fflate-0.8.2.min.js"
|
||||
"fflate-0.8.2.min.js",
|
||||
"croppie.min.js"
|
||||
];
|
Loading…
Reference in New Issue
Block a user