mirror of
https://github.com/HeyPuter/puter
synced 2024-11-14 22:06:00 +00:00
implement public profiles
This commit is contained in:
parent
eae51e945b
commit
2b821c3e73
@ -27,7 +27,7 @@ import UIWindowManageSessions from '../UIWindowManageSessions.js';
|
||||
export default {
|
||||
id: 'account',
|
||||
title_i18n_key: 'account',
|
||||
icon: 'user.svg',
|
||||
icon: 'gear.svg',
|
||||
html: () => {
|
||||
let h = `<h1>${i18n('account')}</h1>`;
|
||||
|
||||
|
90
src/gui/src/UI/Settings/UITabProfile.js
Normal file
90
src/gui/src/UI/Settings/UITabProfile.js
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
// About
|
||||
export default {
|
||||
id: 'profile',
|
||||
title_i18n_key: 'profile',
|
||||
icon: 'user.svg',
|
||||
html: () => {
|
||||
let h = ``;
|
||||
|
||||
h += `<div style="overflow:hidden;">`;
|
||||
// profile picture
|
||||
h += `<div class="profile-picture change-profile-picture" style="background-image:url(${window.user.profile.picture ? window.user.profile.picture : window.icons['profile-black.svg']}); width:100px; height:100px; margin: 20px auto 0 auto;"></div>`;
|
||||
|
||||
// name
|
||||
h += `<label for="name" style="display:block;">${i18n('name')}</label>`;
|
||||
h += `<input type="text" id="name" value="${window.user.profile.name ?? ''}" style="width:100%; margin-bottom:10px;"/>`;
|
||||
|
||||
// bio
|
||||
h += `<label for="bio" style="display:block;">${i18n('bio')}</label>`;
|
||||
h += `<textarea id="bio" class="form-input" style="width:100%; height:200px; box-sizing: border-box; margin-bottom:10px; resize: none;">${window.user.profile.bio ?? ''}</textarea>`;
|
||||
|
||||
// save button
|
||||
h += `<button class="button button-primary save-profile" style="margin: 0 auto; display:block;">${i18n('save')}</button>`;
|
||||
|
||||
h += `</div>`;
|
||||
|
||||
return h;
|
||||
},
|
||||
init: ($el_window) => {
|
||||
|
||||
$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: ['image/*'],
|
||||
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() {
|
||||
const base64data = reader.result;
|
||||
console.log(base64data)
|
||||
// update profile picture
|
||||
$el_window.find('.profile-picture').css('background-image', 'url(' + html_encode(base64data) + ')');
|
||||
// update profile picture
|
||||
update_profile(window.user.username, {picture: base64data})
|
||||
}
|
||||
})
|
||||
|
||||
$el_window.find('.save-profile').on('click', async function (e) {
|
||||
const name = $el_window.find('#name').val();
|
||||
const bio = $el_window.find('#bio').val();
|
||||
update_profile(window.user.username, {name, bio})
|
||||
});
|
||||
},
|
||||
};
|
@ -34,7 +34,7 @@ async function UIWindowSettings(options){
|
||||
h += `<div class="settings-container">`;
|
||||
h += `<div class="settings">`;
|
||||
// sidebar toggle
|
||||
h += `<button class="sidebar-toggle hidden-lg hidden-xl"><div class="sidebar-toggle-button"><span></span><span></span><span></span></div></button>`;
|
||||
h += `<button class="sidebar-toggle hidden-md hidden-lg hidden-xl"><div class="sidebar-toggle-button"><span></span><span></span><span></span></div></button>`;
|
||||
// sidebar
|
||||
h += `<div class="settings-sidebar disable-user-select disable-context-menu">`;
|
||||
// sidebar items
|
||||
|
@ -1005,7 +1005,7 @@ async function UIDesktop(options){
|
||||
ht += `<div class="toolbar-btn qr-btn" title="QR code" style="background-image:url(${window.icons['qr.svg']})"></div>`;
|
||||
|
||||
// user options menu
|
||||
ht += `<div class="toolbar-btn user-options-menu-btn" style="background-image:url(${window.icons['profile.svg']})">`;
|
||||
ht += `<div class="toolbar-btn user-options-menu-btn profile-pic" style="background-image:url(${window.user.profile.picture ? window.user.profile.picture : window.icons['profile.svg']})">`;
|
||||
h += `<span class="user-options-menu-username">${window.user.username}</span>`;
|
||||
ht += `</div>`;
|
||||
ht += `</div>`;
|
||||
@ -1226,6 +1226,10 @@ $(document).on('click', '.user-options-menu-btn', async function(e){
|
||||
|
||||
// create menu items
|
||||
users_arr.forEach(l_user => {
|
||||
// construct profile picture image
|
||||
let profile_pic = l_user.profile?.picture ? l_user.profile.picture : window.icons['profile.svg'];
|
||||
profile_pic = `<img src="${profile_pic}" style="width: 20px; height: 20px; border-radius: 50%; margin-right: 5px;">`;
|
||||
|
||||
items.push(
|
||||
{
|
||||
html: l_user.username,
|
||||
|
@ -341,31 +341,6 @@ async function UIWindow(options) {
|
||||
// Detail layout header
|
||||
h += window.explore_table_headers();
|
||||
|
||||
// Maybe render iframe for users public directory
|
||||
(() => {
|
||||
if ( options.is_saveFileDialog || options.is_openFileDialog || options.is_directoryPicker ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! options.path || ! options.path.startsWith('/') ) { // sus
|
||||
return false;
|
||||
}
|
||||
|
||||
const components = options.path.slice(1).split('/');
|
||||
|
||||
if ( components.length === 2 && components[1] === 'Public' ) {
|
||||
const username = components[0];
|
||||
h += `<iframe
|
||||
style="display:block;width:100%"
|
||||
tabindex="-1"
|
||||
frameborder="0"
|
||||
src="http://${username}.at.${window.hosting_domain}"
|
||||
height=150
|
||||
></iframe>
|
||||
`;
|
||||
}
|
||||
})();
|
||||
|
||||
// Add 'This folder is empty' message by default
|
||||
h += `<div class="explorer-empty-message">This folder is empty</div>`;
|
||||
|
||||
|
@ -132,6 +132,7 @@ async function UIWindowDesktopBGSettings(options){
|
||||
$(el_window).find('.desktop-bg-color-block-palette input').on('change', async function(e){
|
||||
window.set_desktop_background({color: $(this).val()})
|
||||
})
|
||||
|
||||
$(el_window).on('file_opened', function(e){
|
||||
let selected_file = Array.isArray(e.detail) ? e.detail[0] : e.detail;
|
||||
const fit = $(el_window).find('.desktop-bg-fit').val();
|
||||
|
@ -158,7 +158,7 @@ body {
|
||||
z-index: -9999;
|
||||
}
|
||||
|
||||
input[type=text], input[type=password], input[type=email], select {
|
||||
input[type=text], input[type=password], input[type=email], select, .form-input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ccc;
|
||||
@ -175,7 +175,7 @@ input[type=text], input[type=password], input[type=email], select {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, select:focus {
|
||||
input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, select:focus, .form-input:focus {
|
||||
border: 2px solid #01a0fd;
|
||||
padding: 7px;
|
||||
}
|
||||
@ -4130,3 +4130,11 @@ fieldset[name=number-code] {
|
||||
.settings-sidebar.active {
|
||||
display: block;
|
||||
}
|
||||
.profile-picture{
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.change-profile-picture{
|
||||
cursor: pointer;
|
||||
}
|
@ -445,6 +445,37 @@ window.update_auth_data = (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({}));
|
||||
}
|
||||
// Ignored
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
const to_storable_user = user => {
|
||||
const storable_user = {...user};
|
||||
delete storable_user.taskbar_items;
|
||||
@ -2419,3 +2450,28 @@ 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];
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
@ -40,7 +40,6 @@ const launch_app = async (options)=>{
|
||||
|
||||
// explorer is a special case
|
||||
if(options.name === 'explorer'){
|
||||
console.log('explorer');
|
||||
app_info = [];
|
||||
}
|
||||
else if(options.app_obj)
|
||||
|
@ -221,6 +221,7 @@ const en = {
|
||||
process_type_app: 'App',
|
||||
process_type_init: 'Init',
|
||||
process_type_ui: 'UI',
|
||||
profile_picture: "Profile Picture",
|
||||
properties: "Properties",
|
||||
public: 'Public',
|
||||
publish: "Publish",
|
||||
|
3
src/gui/src/icons/gear.svg
Normal file
3
src/gui/src/icons/gear.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-gear-wide-connected" viewBox="0 0 16 16">
|
||||
<path d="M7.068.727c.243-.97 1.62-.97 1.864 0l.071.286a.96.96 0 0 0 1.622.434l.205-.211c.695-.719 1.888-.03 1.613.931l-.08.284a.96.96 0 0 0 1.187 1.187l.283-.081c.96-.275 1.65.918.931 1.613l-.211.205a.96.96 0 0 0 .434 1.622l.286.071c.97.243.97 1.62 0 1.864l-.286.071a.96.96 0 0 0-.434 1.622l.211.205c.719.695.03 1.888-.931 1.613l-.284-.08a.96.96 0 0 0-1.187 1.187l.081.283c.275.96-.918 1.65-1.613.931l-.205-.211a.96.96 0 0 0-1.622.434l-.071.286c-.243.97-1.62.97-1.864 0l-.071-.286a.96.96 0 0 0-1.622-.434l-.205.211c-.695.719-1.888.03-1.613-.931l.08-.284a.96.96 0 0 0-1.186-1.187l-.284.081c-.96.275-1.65-.918-.931-1.613l.211-.205a.96.96 0 0 0-.434-1.622l-.286-.071c-.97-.243-.97-1.62 0-1.864l.286-.071a.96.96 0 0 0 .434-1.622l-.211-.205c-.719-.695-.03-1.888.931-1.613l.284.08a.96.96 0 0 0 1.187-1.186l-.081-.284c-.275-.96.918-1.65 1.613-.931l.205.211a.96.96 0 0 0 1.622-.434zM12.973 8.5H8.25l-2.834 3.779A4.998 4.998 0 0 0 12.973 8.5m0-1a4.998 4.998 0 0 0-7.557-3.779l2.834 3.78zM5.048 3.967l-.087.065zm-.431.355A4.98 4.98 0 0 0 3.002 8c0 1.455.622 2.765 1.615 3.678L7.375 8zm.344 7.646.087.065z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
12
src/gui/src/icons/profile-black.svg
Normal file
12
src/gui/src/icons/profile-black.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="59px" height="59px" stroke-width="0.8" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg" color="#3b3e44">
|
||||
<path
|
||||
d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2Z"
|
||||
stroke="#3b3e44" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
<path d="M4.271 18.3457C4.271 18.3457 6.50002 15.5 12 15.5C17.5 15.5 19.7291 18.3457 19.7291 18.3457"
|
||||
stroke="#3b3e44" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
<path
|
||||
d="M12 12C13.6569 12 15 10.6569 15 9C15 7.34315 13.6569 6 12 6C10.3431 6 9 7.34315 9 9C9 10.6569 10.3431 12 12 12Z"
|
||||
stroke="#3b3e44" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 856 B |
@ -19,6 +19,7 @@
|
||||
import { Service } from "../definitions.js";
|
||||
|
||||
import AboutTab from '../UI/Settings/UITabAbout.js';
|
||||
import ProfileTab from '../UI/Settings/UITabProfile.js';
|
||||
import UsageTab from '../UI/Settings/UITabUsage.js';
|
||||
import AccountTab from '../UI/Settings/UITabAccount.js';
|
||||
import SecurityTab from '../UI/Settings/UITabSecurity.js';
|
||||
@ -31,6 +32,7 @@ export class SettingsService extends Service {
|
||||
async _init () {
|
||||
;[
|
||||
AboutTab,
|
||||
ProfileTab,
|
||||
UsageTab,
|
||||
AccountTab,
|
||||
SecurityTab,
|
||||
|
Loading…
Reference in New Issue
Block a user