import path from "../lib/path.js" import UIWindowClaimReferral from "./UIWindowClaimReferral.js" import UIContextMenu from './UIContextMenu.js' import UIItem from './UIItem.js' import UIAlert from './UIAlert.js' import UIWindow from './UIWindow.js' import UIWindowSaveAccount from './UIWindowSaveAccount.js'; import UIWindowDesktopBGSettings from "./UIWindowDesktopBGSettings.js" import UIWindowMyWebsites from "./UIWindowMyWebsites.js" import UIWindowChangePassword from "./UIWindowChangePassword.js" import UIWindowChangeUsername from "./UIWindowChangeUsername.js" import UIWindowFeedback from "./UIWindowFeedback.js" import UIWindowLogin from "./UIWindowLogin.js" import UIWindowQR from "./UIWindowQR.js" import UIWindowRefer from "./UIWindowRefer.js" import UITaskbar from "./UITaskbar.js" async function UIDesktop(options){ let h = ''; // connect socket. window.socket = io(gui_origin + '/', { query: { auth_token: auth_token } }); window.socket.on('error', (error) => { console.error('GUI Socket Error:', error); }); window.socket.on('connect', function(){ console.log('GUI Socket: Connected', window.socket.id); }); window.socket.on('reconnect', function(){ console.log('GUI Socket: Reconnected', window.socket.id); }); window.socket.on('disconnect', () => { console.log('GUI Socket: Disconnected'); }); window.socket.on('reconnect', (attempt) => { console.log('GUI Socket: Reconnection', attempt); }); window.socket.on('reconnect_attempt', (attempt) => { console.log('GUI Socket: Reconnection Attemps', attempt); }); window.socket.on('reconnect_error', (error) => { console.log('GUI Socket: Reconnection Error', error); }); window.socket.on('reconnect_failed', () => { console.log('GUI Socket: Reconnection Failed'); }); window.socket.on('error', (error) => { console.error('GUI Socket Error:', error); }); socket.on('upload.progress', (msg) => { if(window.progress_tracker[msg.operation_id]){ window.progress_tracker[msg.operation_id].cloud_uploaded += msg.loaded_diff if(window.progress_tracker[msg.operation_id][msg.item_upload_id]){ window.progress_tracker[msg.operation_id][msg.item_upload_id].cloud_uploaded = msg.loaded; } } }); socket.on('download.progress', (msg) => { if(window.progress_tracker[msg.operation_id]){ if(window.progress_tracker[msg.operation_id][msg.item_upload_id]){ window.progress_tracker[msg.operation_id][msg.item_upload_id].downloaded = msg.loaded; window.progress_tracker[msg.operation_id][msg.item_upload_id].total = msg.total; } } }); socket.on('trash.is_empty', async (msg) => { $(`.item[data-path="${html_encode(trash_path)}" i]`).find('.item-icon > img').attr('src', msg.is_empty ? window.icons['trash.svg'] : window.icons['trash-full.svg']); $(`.window[data-path="${html_encode(trash_path)}" i]`).find('.window-head-icon').attr('src', msg.is_empty ? window.icons['trash.svg'] : window.icons['trash-full.svg']); // empty trash windows if needed if(msg.is_empty) $(`.window[data-path="${html_encode(trash_path)}" i]`).find('.item-container').empty(); }) socket.on('app.opened', async (app) => { // don't update if this is the original client that initiated the action if(app.original_client_socket_id === window.socket.id) return; // add the app to the beginning of the array launch_apps.recent.unshift(app); // dedupe the array by uuid, uid, and id launch_apps.recent = _.uniqBy(launch_apps.recent, 'name'); // limit to 5 launch_apps.recent = launch_apps.recent.slice(0, window.launch_recent_apps_count); }) socket.on('item.removed', async (item) => { // don't update if this is the original client that initiated the action if(item.original_client_socket_id === window.socket.id) return; // don't remove items if this was a descendants_only operation if(item.descendants_only) return; // hide all UIItems with matching uids $(`.item[data-path='${item.path}']`).fadeOut(150, function(){ // close all windows with matching uids // $('.window-' + item.uid).close(); // close all windows that belong to a descendant of this item // todo this has to be case-insensitive but the `i` selector doesn't work on ^= $(`.window[data-path^="${item.path}/"]`).close(); }); }) socket.on('item.updated', async (item) => { // Don't update if this is the original client that initiated the action if(item.original_client_socket_id === window.socket.id) return; // Update matching items // set new item name $(`.item[data-uid='${html_encode(item.uid)}'] .item-name`).html(html_encode(truncate_filename(item.name, TRUNCATE_LENGTH)).replaceAll(' ', ' ')); // Set new icon const new_icon = (item.is_dir ? window.icons['folder.svg'] : (await item_icon(item)).image); $(`.item[data-uid='${item.uid}']`).find('.item-icon-thumb').attr('src', new_icon); $(`.item[data-uid='${item.uid}']`).find('.item-icon-icon').attr('src', new_icon); // Set new data-name $(`.item[data-uid='${item.uid}']`).attr('data-name', html_encode(item.name)); $(`.window-${item.uid}`).attr('data-name', html_encode(item.name)); // Set new title attribute $(`.item[data-uid='${item.uid}']`).attr('title', html_encode(item.name)); $(`.window-${options.uid}`).attr('title', html_encode(item.name)); // Set new value for item-name-editor $(`.item[data-uid='${item.uid}'] .item-name-editor`).val(html_encode(item.name)); $(`.item[data-uid='${item.uid}'] .item-name`).attr('title', html_encode(item.name)); // Set new data-path const new_path = item.path; $(`.item[data-uid='${item.uid}']`).attr('data-path', new_path); $(`.window-${item.uid}`).attr('data-path', new_path); // Update all elements that have matching paths $(`[data-path="${html_encode(item.old_path)}" i]`).each(function(){ $(this).attr('data-path', new_path) if($(this).hasClass('window-navbar-path-dirname')) $(this).text(item.name); }); // Update all elements whose paths start with old_path $(`[data-path^="${html_encode(item.old_path) + '/'}"]`).each(function(){ const new_el_path = _.replace($(this).attr('data-path'), item.old_path + '/', new_path+'/'); $(this).attr('data-path', new_el_path); }); // Update all exact-matching windows $(`.window-${item.uid}`).each(function(){ update_window_path(this, new_path); }) // Set new name for matching open windows $(`.window-${item.uid} .window-head-title`).text(item.name); // Re-sort all matching item containers $(`.item[data-uid='${item.uid}']`).parent('.item-container').each(function(){ sort_items(this, $(this).closest('.item-container').attr('data-sort_by'), $(this).closest('.item-container').attr('data-sort_order')); }) }) socket.on('item.moved', async (resp) => { let fsentry = resp; // Notify all apps that are watching this item sendItemChangeEventToWatchingApps(fsentry.uid, { event: 'moved', uid: fsentry.uid, name: fsentry.name, }) // don't update if this is the original client that initiated the action if(resp.original_client_socket_id === window.socket.id) return; let dest_path = path.dirname(fsentry.path); let metadata = fsentry.metadata; // path must use the real name from DB fsentry.path = fsentry.path; // update all shortcut_to_path $(`.item[data-shortcut_to_path="${html_encode(resp.old_path)}" i]`).attr(`data-shortcut_to_path`, html_encode(fsentry.path)); // remove all items with matching uids $(`.item[data-uid='${fsentry.uid}']`).fadeOut(150, function(){ // find all parent windows that contain this item let parent_windows = $(`.item[data-uid='${fsentry.uid}']`).closest('.window'); // remove this item $(this).removeItems(); // update parent windows' item counts $(parent_windows).each(function(index){ update_explorer_footer_item_count(this); update_explorer_footer_selected_items_count(this) }); }) // if trashing, close windows of trashed items and its descendants if(dest_path === trash_path){ $(`.window[data-path="${html_encode(resp.old_path)}" i]`).close(); // todo this has to be case-insensitive but the `i` selector doesn't work on ^= $(`.window[data-path^="${html_encode(resp.old_path)}/"]`).close(); } // update all paths of its and its descendants' open windows else{ // todo this has to be case-insensitive but the `i` selector doesn't work on ^= $(`.window[data-path^="${html_encode(resp.old_path)}/"], .window[data-path="${html_encode(resp.old_path)}" i]`).each(function(){ update_window_path(this, $(this).attr('data-path').replace(resp.old_path, fsentry.path)); }) } if(dest_path === trash_path){ $(`.item[data-uid="${fsentry.uid}"]`).find('.item-is-shared').fadeOut(300); // if trashing dir... if(fsentry.is_dir){ // remove website badge $(`.mywebsites-dir-path[data-uuid="${fsentry.uid}"]`).remove(); // remove the website badge from all instances of the dir $(`.item[data-uid="${fsentry.uid}"]`).find('.item-has-website-badge').fadeOut(300); // remove File Rrequest Token // todo, some client-side check to see if this dir has an FR associated with it before sending a whole ajax req } } // if replacing an existing item, remove the old item that was just replaced if(fsentry.overwritten_uid !== undefined) $(`.item[data-uid=${fsentry.overwritten_uid}]`).removeItems(); // if this is trash, get original name from item metadata fsentry.name = (metadata && metadata.original_name) ? metadata.original_name : fsentry.name; // create new item on matching containers UIItem({ appendTo: $(`.item-container[data-path='${html_encode(dest_path)}' i]`), immutable: fsentry.immutable, uid: fsentry.uid, path: fsentry.path, icon: await item_icon(fsentry), name: (dest_path === trash_path) ? metadata.original_name : fsentry.name, is_dir: fsentry.is_dir, size: fsentry.size, type: fsentry.type, modified: fsentry.modified, is_selected: false, is_shared: (dest_path === trash_path) ? false : fsentry.is_shared, is_shortcut: fsentry.is_shortcut, shortcut_to: fsentry.shortcut_to, shortcut_to_path: fsentry.shortcut_to_path, // has_website: $(el_item).attr('data-has_website') === '1', metadata: JSON.stringify(fsentry.metadata) ?? '', }); if(fsentry.parent_dirs_created && fsentry.parent_dirs_created.length > 0){ // this operation may have created some missing directories, // see if any of the directories in the path of this file is new AND // if these new path have any open parents that need to be updated fsentry.parent_dirs_created.forEach(async dir => { let item_container = $(`.item-container[data-path='${html_encode(path.dirname(dir.path))}' i]`); if(item_container.length > 0 && $(`.item[data-path="${html_encode(dir.path)}" i]`).length === 0){ UIItem({ appendTo: item_container, immutable: false, uid: dir.uid, path: dir.path, icon: await item_icon(dir), name: dir.name, size: dir.size, type: dir.type, modified: dir.modified, is_dir: true, is_selected: false, is_shared: dir.is_shared, has_website: false, }); } sort_items(item_container, $(item_container).attr('data-sort_by'), $(item_container).attr('data-sort_order')); }); } //sort each container $(`.item-container[data-path='${html_encode(dest_path)}' i]`).each(function(){ sort_items(this, $(this).attr('data-sort_by'), $(this).attr('data-sort_order')) }) }); socket.on('user.email_confirmed', (msg) => { // don't update if this is the original client that initiated the action if(msg.original_client_socket_id === window.socket.id) return; refresh_user_data(window.auth_token); }); socket.on('item.renamed', async (item) => { // Notify all apps that are watching this item sendItemChangeEventToWatchingApps(item.uid, { event: 'rename', uid: item.uid, // path: item.path, new_name: item.name, // old_path: item.old_path, }) // Don't update if this is the original client that initiated the action if(item.original_client_socket_id === window.socket.id) return; // Update matching items // Set new item name $(`.item[data-uid='${html_encode(item.uid)}'] .item-name`).html(html_encode(truncate_filename(item.name, TRUNCATE_LENGTH)).replaceAll(' ', ' ')); // Set new icon const new_icon = (item.is_dir ? window.icons['folder.svg'] : (await item_icon(item)).image); $(`.item[data-uid='${item.uid}']`).find('.item-icon-icon').attr('src', new_icon); // Set new data-name $(`.item[data-uid='${item.uid}']`).attr('data-name', html_encode(item.name)); $(`.window-${item.uid}`).attr('data-name', html_encode(item.name)); // Set new title attribute $(`.item[data-uid='${item.uid}']`).attr('title', html_encode(item.name)); $(`.window-${options.uid}`).attr('title', html_encode(item.name)); // Set new value for item-name-editor $(`.item[data-uid='${item.uid}'] .item-name-editor`).val(html_encode(item.name)); $(`.item[data-uid='${item.uid}'] .item-name`).attr('title', html_encode(item.name)); // Set new data-path const new_path = item.path; $(`.item[data-uid='${item.uid}']`).attr('data-path', new_path); $(`.window-${item.uid}`).attr('data-path', new_path); // Update all elements that have matching paths $(`[data-path="${html_encode(item.old_path)}" i]`).each(function(){ $(this).attr('data-path', new_path) if($(this).hasClass('window-navbar-path-dirname')) $(this).text(item.name); }); // Update all elements whose paths start with old_path $(`[data-path^="${html_encode(item.old_path) + '/'}"]`).each(function(){ const new_el_path = _.replace($(this).attr('data-path'), item.old_path + '/', new_path+'/'); $(this).attr('data-path', new_el_path); }); // Update all exact-matching windows $(`.window-${item.uid}`).each(function(){ update_window_path(this, new_path); }) // Set new name for matching open windows $(`.window-${item.uid} .window-head-title`).text(item.name); // Re-sort all matching item containers $(`.item[data-uid='${item.uid}']`).parent('.item-container').each(function(){ sort_items(this, $(this).closest('.item-container').attr('data-sort_by'), $(this).closest('.item-container').attr('data-sort_order')); }) }); socket.on('item.added', async (item) => { // if item is empty, don't proceed if(_.isEmpty(item)) return; // Notify all apps that are watching this item sendItemChangeEventToWatchingApps(item.uid, { event: 'write', uid: item.uid, // path: item.path, new_size: item.size, modified: item.modified, // old_path: item.old_path, }); // Don't update if this is the original client that initiated the action if(item.original_client_socket_id === window.socket.id) return; // Update replaced items with matching uids if(item.overwritten_uid){ $(`.item[data-uid='${item.overwritten_uid}']`).attr({ 'data-immutable': item.immutable, 'data-path': item.path, 'data-name': item.name, 'data-size': item.size, 'data-modified': item.modified, 'data-is_shared': item.is_shared, 'data-type': item.type, }) // set new icon const new_icon = (item.is_dir ? window.icons['folder.svg'] : (await item_icon(item)).image); $(`.item[data-uid="${item.overwritten_uid}"]`).find('.item-icon > img').attr('src', new_icon); //sort each window $(`.item-container[data-path='${html_encode(item.dirpath)}' i]`).each(function(){ sort_items(this, $(this).attr('data-sort_by'), $(this).attr('data-sort_order')) }) } else{ UIItem({ appendTo: $(`.item-container[data-path='${html_encode(item.dirpath)}' i]`), uid: item.uid, immutable: item.immutable, associated_app_name: item.associated_app?.name, path: item.path, icon: await item_icon(item), name: item.name, size: item.size, type: item.type, modified: item.modified, is_dir: item.is_dir, is_shared: item.is_shared, is_shortcut: item.is_shortcut, associated_app_name: item.associated_app?.name, shortcut_to: item.shortcut_to, shortcut_to_path: item.shortcut_to_path, }); //sort each window $(`.item-container[data-path='${html_encode(item.dirpath)}' i]`).each(function(){ sort_items(this, $(this).attr('data-sort_by'), $(this).attr('data-sort_order')) }) } }); // Hidden file dialog h += `
`; h += ``; // Desktop // If desktop is not in fullpage/embedded mode, we hide it until files and directories are loaded and then fade in the UI // This gives a calm and smooth experience for the user h += `You have open apps. Are you sure you want to log out?
`, buttons:[ { label: 'Close Windows and Log Out', type: 'primary', }, { label: 'Cancel' }, ] }) if(alert_resp === 'Close Windows and Log Out') logout(); } // no open windows else logout(); } }, ] }); }) $(document).on('click', '.fullscreen-btn', async function (e) { if(!is_fullscreen()) { var elem = document.documentElement; if (elem.requestFullscreen) { elem.requestFullscreen(); } else if (elem.webkitRequestFullscreen) { /* Safari */ elem.webkitRequestFullscreen(); } else if (elem.mozRequestFullScreen) { /* moz */ elem.mozRequestFullScreen(); } else if (elem.msRequestFullscreen) { /* IE11 */ elem.msRequestFullscreen(); } } else{ if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } } }) $(document).on('click', '.close-launch-popover', function(){ $(".launch-popover").closest('.popover').fadeOut(200, function(){ $(".launch-popover").closest('.popover').remove(); }); }); $(document).on('click', '.toolbar-puter-logo', function(){ // launch the about app launch_app({name: 'about', window_options:{ single_instance: true, }}); }) $(document).on('click', '.user-options-create-account-btn', async function(e){ UIWindowSaveAccount({ send_confirmation_code: false, default_username: window.user.username, }); }) $(document).on('click', '.refer-btn', async function(e){ UIWindowRefer(); }) $(document).on('click', '.start-app', async function(e){ launch_app({ name: $(this).attr('data-app-name') }) // close popovers $(".popover").fadeOut(200, function(){ $(".popover").remove(); }); }) $(document).on('click', '.user-options-login-btn', async function(e){ const alert_resp = await UIAlert({ message: `Save session before exiting!You are in a temporary session and logging into another account will erase all data in your current session.
`, buttons:[ { label: 'Save session', value: 'save-session', type: 'primary', }, { label: 'Log into another account anyway', value: 'login', }, { label: 'Cancel' }, ] }) if(alert_resp === 'save-session'){ let saved = await UIWindowSaveAccount({ send_confirmation_code: false, }); if(saved) UIWindowLogin({show_signup_button: false, reload_on_success: true}); }else if (alert_resp === 'login'){ UIWindowLogin({ show_signup_button: false, reload_on_success: true, window_options: { backdrop: true, close_on_backdrop_click: false, } }); } }) $(document).on('click mousedown', '.launch-search, .launch-popover', function(e){ $(this).focus(); e.stopPropagation(); e.preventDefault(); // don't let click bubble up to window e.stopImmediatePropagation(); }) $(document).on('focus', '.launch-search', function(e){ // remove all selected items in start menu $('.launch-app-selected').removeClass('launch-app-selected'); // scroll popover to top $('.launch-popover').scrollTop(0); }) $(document).on('change keyup keypress keydown paste', '.launch-search', function(e){ // search launch_apps.recommended for query const query = $(this).val().toLowerCase(); if(query === ''){ $('.launch-search-clear').hide(); $(`.start-app-card`).show(); $('.launch-apps-recent').show(); $('.start-section-heading').show(); }else{ $('.launch-apps-recent').hide(); $('.start-section-heading').hide(); $('.launch-search-clear').show(); launch_apps.recommended.forEach((app)=>{ if(app.title.toLowerCase().includes(query.toLowerCase())){ $(`.start-app-card[data-name="${app.name}"]`).show(); }else{ $(`.start-app-card[data-name="${app.name}"]`).hide(); } }) } }) $(document).on('click', '.launch-search-clear', function(e){ $('.launch-search').val(''); $('.launch-search').trigger('change'); $('.launch-search').focus(); }) document.addEventListener('fullscreenchange', (event) => { // document.fullscreenElement will point to the element that // is in fullscreen mode if there is one. If there isn't one, // the value of the property is null. if (document.fullscreenElement) { $('.fullscreen-btn').css('background-image', `url(${window.icons['shrink.svg']})`); $('.fullscreen-btn').attr('title', 'Exit Full Screen'); $('#clock').show(); } else { $('.fullscreen-btn').css('background-image', `url(${window.icons['fullscreen.svg']})`); $('.fullscreen-btn').attr('title', 'Enter Full Screen'); $('#clock').hide(); } }) window.set_desktop_background = function(options){ if(options.fit){ let fit = options.fit; if(fit === 'cover' || fit === 'contain'){ $('body').css('background-size', fit); $('body').css('background-repeat', `no-repeat`); $('body').css('background-position', `center center`); } else if(fit === 'center'){ $('body').css('background-size', 'auto'); $('body').css('background-repeat', `no-repeat`); $('body').css('background-position', `center center`); } else if( fit === 'repeat'){ $('body').css('background-size', `auto`); $('body').css('background-repeat', `repeat`); } window.desktop_bg_fit = fit; } if(options.url){ $('body').css('background-image', `url(${options.url})`); window.desktop_bg_url = options.url; window.desktop_bg_color = undefined; } else if(options.color){ $('body').css({ 'background-image': `none`, 'background-color': options.color, }); window.desktop_bg_color = options.color; window.desktop_bg_url = undefined; } } window.update_taskbar = function(){ let items = [] $('.taskbar-item-sortable[data-keep-in-taskbar="true"]').each(function(index){ items.push({ name: $( this ).attr('data-app'), type: 'app', }) }) // update taskbar in the server-side $.ajax({ url: api_origin + "/update-taskbar-items", type: 'POST', data: JSON.stringify({ items: items, }), async: true, contentType: "application/json", headers: { "Authorization": "Bearer "+auth_token }, }) } window.remove_taskbar_item = function(item){ $(item).find('*').fadeOut(100, function(){}); $(item).animate({width: 0}, 200, function(){ $(item).remove(); }) } window.enter_fullpage_mode = (el_window)=>{ $('.taskbar').hide(); $(el_window).find('.window-head').hide(); $('body').addClass('fullpage-mode'); $(el_window).css({ width: '100%', height: '100%', top: toolbar_height + 'px', left: 0, 'border-radius': 0, }); } window.exit_fullpage_mode = (el_window)=>{ $('body').removeClass('fullpage-mode'); window.taskbar_height = window.default_taskbar_height; $('.taskbar').css('height', window.taskbar_height); $('.taskbar').show(); refresh_item_container($('.desktop.item-container'), {fadeInItems: true}); $(el_window).removeAttr('data-is_fullpage'); if(el_window){ reset_window_size_and_position(el_window) $(el_window).find('.window-head').show(); } // reset dektop height to take into account the taskbar height $('.desktop').css('height', `calc(100vh - ${window.taskbar_height + window.toolbar_height}px)`); // hide the 'Show Desktop' button in toolbar $('.show-desktop-btn').hide(); // refresh desktop background refresh_desktop_background(); } window.reset_window_size_and_position = (el_window)=>{ $(el_window).css({ width: 680, height: 380, 'border-radius': window_border_radius, top: 'calc(50% - 190px)', left: 'calc(50% - 340px)', }); } export default UIDesktop;