refactor item_icon

This commit is contained in:
Nariman Jelveh 2024-06-19 18:39:32 -07:00
parent 219cb63b73
commit 7006dcc31c
8 changed files with 229 additions and 201 deletions

View File

@ -30,6 +30,7 @@ import path from "./lib/path.js";
import UIContextMenu from './UI/UIContextMenu.js';
import update_mouse_position from './helpers/update_mouse_position.js';
import launch_app from './helpers/launch_app.js';
import item_icon from './helpers/item_icon.js';
/**
* In Puter, apps are loaded in iframes and communicate with the graphical user interface (GUI), and each other, using the postMessage API.
@ -1063,7 +1064,7 @@ window.addEventListener('message', async (event) => {
immutable: res.immutable,
associated_app_name: res.associated_app?.name,
path: target_path,
icon: await window.item_icon(res),
icon: await item_icon(res),
name: path.basename(target_path),
uid: res.uid,
size: res.size,

View File

@ -39,6 +39,7 @@ import UIWindowTaskManager from "./UIWindowTaskManager.js"
import truncate_filename from '../helpers/truncate_filename.js';
import UINotification from "./UINotification.js"
import launch_app from "../helpers/launch_app.js"
import item_icon from "../helpers/item_icon.js"
async function UIDesktop(options){
let h = '';
@ -135,7 +136,7 @@ async function UIDesktop(options){
UIWindow({
path: '/' + notification.fields.username,
title: path.basename(item_path),
icon: await window.item_icon({is_dir: true, path: item_path}),
icon: await item_icon({is_dir: true, path: item_path}),
is_dir: true,
app: 'explorer',
});
@ -217,7 +218,7 @@ async function UIDesktop(options){
$(`.item[data-uid='${html_encode(item.uid)}'] .item-name`).html(html_encode(truncate_filename(item.name)).replaceAll(' ', ' '));
// Set new icon
const new_icon = (item.is_dir ? window.icons['folder.svg'] : (await window.item_icon(item)).image);
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);
@ -338,7 +339,7 @@ async function UIDesktop(options){
immutable: fsentry.immutable,
uid: fsentry.uid,
path: fsentry.path,
icon: await window.item_icon(fsentry),
icon: await item_icon(fsentry),
name: (dest_path === window.trash_path) ? metadata.original_name : fsentry.name,
is_dir: fsentry.is_dir,
size: fsentry.size,
@ -366,7 +367,7 @@ async function UIDesktop(options){
immutable: false,
uid: dir.uid,
path: dir.path,
icon: await window.item_icon(dir),
icon: await item_icon(dir),
name: dir.name,
size: dir.size,
type: dir.type,
@ -421,7 +422,7 @@ async function UIDesktop(options){
$(`.item[data-uid='${html_encode(item.uid)}'] .item-name`).html(html_encode(truncate_filename(item.name)).replaceAll(' ', ' '));
// Set new icon
const new_icon = (item.is_dir ? window.icons['folder.svg'] : (await window.item_icon(item)).image);
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
@ -498,7 +499,7 @@ async function UIDesktop(options){
'data-type': item.type,
})
// set new icon
const new_icon = (item.is_dir ? window.icons['folder.svg'] : (await window.item_icon(item)).image);
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
@ -513,7 +514,7 @@ async function UIDesktop(options){
immutable: item.immutable,
associated_app_name: item.associated_app?.name,
path: item.path,
icon: await window.item_icon(item),
icon: await item_icon(item),
name: item.name,
size: item.size,
type: item.type,
@ -1007,7 +1008,7 @@ async function UIDesktop(options){
UIWindow({
path: predefined_path,
title: path.basename(predefined_path),
icon: await window.item_icon({is_dir: true, path: predefined_path}),
icon: await item_icon({is_dir: true, path: predefined_path}),
// todo
// uid: $(el_item).attr('data-uid'),
is_dir: true,

View File

@ -30,6 +30,7 @@ import UIWindowSaveAccount from './UIWindowSaveAccount.js';
import UIWindowEmailConfirmationRequired from './UIWindowEmailConfirmationRequired.js';
import launch_app from "../helpers/launch_app.js"
import UIWindowShare from './UIWindowShare.js';
import item_icon from '../helpers/item_icon.js';
const el_body = document.getElementsByTagName('body')[0];
@ -2343,7 +2344,7 @@ $(document).on('click', '.window-sidebar-item', async function(e){
UIWindow({
path: item_path,
title: path.basename(item_path),
icon: await window.item_icon({is_dir: true, path: item_path}),
icon: await item_icon({is_dir: true, path: item_path}),
// todo
// uid: $(el_item).attr('data-uid'),
is_dir: true,
@ -2405,7 +2406,7 @@ $(document).on('contextmenu taphold', '.window-sidebar-item', function(event){
UIWindow({
path: item_path,
title: path.basename(item_path),
icon: await window.item_icon({is_dir: true, path: item_path}),
icon: await item_icon({is_dir: true, path: item_path}),
// todo
// uid: $(el_item).attr('data-uid'),
is_dir: true,

View File

@ -18,18 +18,17 @@
*/
import path from "./lib/path.js"
import mime from "./lib/mime.js";
import UIAlert from './UI/UIAlert.js'
import UIItem from './UI/UIItem.js'
import UIWindowLogin from './UI/UIWindowLogin.js';
import UIWindowSaveAccount from './UI/UIWindowSaveAccount.js';
import update_username_in_gui from './helpers/update_username_in_gui.js';
import update_title_based_on_uploads from './helpers/update_title_based_on_uploads.js';
import content_type_to_icon from './helpers/content_type_to_icon.js';
import truncate_filename from './helpers/truncate_filename.js';
import UIWindowProgress from './UI/UIWindowProgress.js';
import globToRegExp from "./helpers/globToRegExp.js";
import get_html_element_from_options from "./helpers/get_html_element_from_options.js";
import item_icon from "./helpers/item_icon.js";
window.is_auth = ()=>{
if(localStorage.getItem("auth_token") === null || window.auth_token === null)
@ -624,187 +623,6 @@ window.sendItemChangeEventToWatchingApps = function(item_uid, event_data){
}
}
/**
* Assigns an icon to a filesystem entry based on its properties such as name, type,
* and whether it's a directory, app, trashed, or specific file type.
*
* @function item_icon
* @global
* @async
* @param {Object} fsentry - A filesystem entry object. It may contain various properties
* like name, type, path, associated_app, thumbnail, is_dir, and metadata, depending on
* the type of filesystem entry.
*/
window.item_icon = async (fsentry)=>{
// --------------------------------------------------
// If this file is Trashed then set the name to the original name of the file before it was trashed
// --------------------------------------------------
if(fsentry.path?.startsWith(window.trash_path + '/')){
if(fsentry.metadata){
try{
let metadata = JSON.parse(fsentry.metadata);
fsentry.name = (metadata && metadata.original_name) ? metadata.original_name : fsentry.name
}
catch(e){
// Ignored
}
}
}
// --------------------------------------------------
// thumbnail
// --------------------------------------------------
if(fsentry.thumbnail){
return {image: fsentry.thumbnail, type: 'thumb'};
}
// --------------------------------------------------
// app icon
// --------------------------------------------------
else if(fsentry.associated_app && fsentry.associated_app?.name){
if(fsentry.associated_app.icon)
return {image: fsentry.associated_app.icon, type: 'icon'};
else
return {image: window.icons['app.svg'], type: 'icon'};
}
// --------------------------------------------------
// Trash
// --------------------------------------------------
else if(fsentry.shortcut_to_path && fsentry.shortcut_to_path === window.trash_path){
// get trash image, this is needed to get the correct empty vs full trash icon
let trash_img = $(`.item[data-path="${html_encode(window.trash_path)}" i] .item-icon-icon`).attr('src')
// if trash_img is undefined that's probably because trash wasn't added anywhere, do a direct lookup to see if trash is empty or no
if(!trash_img){
let trashstat = await puter.fs.stat(window.trash_path);
if(trashstat.is_empty !== undefined && trashstat.is_empty === true)
trash_img = window.icons['trash.svg'];
else
trash_img = window.icons['trash-full.svg'];
}
return {image: trash_img, type: 'icon'};
}
// --------------------------------------------------
// Directories
// --------------------------------------------------
else if(fsentry.is_dir){
// System Directories
if(fsentry.path === window.docs_path)
return {image: window.icons['folder-documents.svg'], type: 'icon'};
else if (fsentry.path === window.pictures_path)
return { image: window.icons['folder-pictures.svg'], type: 'icon' };
else if (fsentry.path === window.home_path)
return { image: window.icons['folder-home.svg'], type: 'icon' };
else if (fsentry.path === window.videos_path)
return { image: window.icons['folder-videos.svg'], type: 'icon' };
else if (fsentry.path === window.desktop_path)
return { image: window.icons['folder-desktop.svg'], type: 'icon' };
else if (fsentry.path === window.public_path)
return { image: window.icons['folder-public.svg'], type: 'icon' };
// regular directories
else
return {image: window.icons['folder.svg'], type: 'icon'};
}
// --------------------------------------------------
// Match icon by file extension
// --------------------------------------------------
// *.doc
else if(fsentry.name.toLowerCase().endsWith('.doc')){
return {image: window.icons['file-doc.svg'], type: 'icon'};
}
// *.docx
else if(fsentry.name.toLowerCase().endsWith('.docx')){
return {image: window.icons['file-docx.svg'], type: 'icon'};
}
// *.exe
else if(fsentry.name.toLowerCase().endsWith('.exe')){
return {image: window.icons['file-exe.svg'], type: 'icon'};
}
// *.gz
else if(fsentry.name.toLowerCase().endsWith('.gz')){
return {image: window.icons['file-gzip.svg'], type: 'icon'};
}
// *.jar
else if(fsentry.name.toLowerCase().endsWith('.jar')){
return {image: window.icons['file-jar.svg'], type: 'icon'};
}
// *.java
else if(fsentry.name.toLowerCase().endsWith('.java')){
return {image: window.icons['file-java.svg'], type: 'icon'};
}
// *.jsp
else if(fsentry.name.toLowerCase().endsWith('.jsp')){
return {image: window.icons['file-jsp.svg'], type: 'icon'};
}
// *.log
else if(fsentry.name.toLowerCase().endsWith('.log')){
return {image: window.icons['file-log.svg'], type: 'icon'};
}
// *.mp3
else if(fsentry.name.toLowerCase().endsWith('.mp3')){
return {image: window.icons['file-mp3.svg'], type: 'icon'};
}
// *.rb
else if(fsentry.name.toLowerCase().endsWith('.rb')){
return {image: window.icons['file-ruby.svg'], type: 'icon'};
}
// *.rss
else if(fsentry.name.toLowerCase().endsWith('.rss')){
return {image: window.icons['file-rss.svg'], type: 'icon'};
}
// *.rtf
else if(fsentry.name.toLowerCase().endsWith('.rtf')){
return {image: window.icons['file-rtf.svg'], type: 'icon'};
}
// *.sketch
else if(fsentry.name.toLowerCase().endsWith('.sketch')){
return {image: window.icons['file-sketch.svg'], type: 'icon'};
}
// *.sql
else if(fsentry.name.toLowerCase().endsWith('.sql')){
return {image: window.icons['file-sql.svg'], type: 'icon'};
}
// *.tif
else if(fsentry.name.toLowerCase().endsWith('.tif')){
return {image: window.icons['file-tif.svg'], type: 'icon'};
}
// *.tiff
else if(fsentry.name.toLowerCase().endsWith('.tiff')){
return {image: window.icons['file-tiff.svg'], type: 'icon'};
}
// *.wav
else if(fsentry.name.toLowerCase().endsWith('.wav')){
return {image: window.icons['file-wav.svg'], type: 'icon'};
}
// *.cpp
else if(fsentry.name.toLowerCase().endsWith('.cpp')){
return {image: window.icons['file-cpp.svg'], type: 'icon'};
}
// *.pptx
else if(fsentry.name.toLowerCase().endsWith('.pptx')){
return {image: window.icons['file-pptx.svg'], type: 'icon'};
}
// *.psd
else if(fsentry.name.toLowerCase().endsWith('.psd')){
return {image: window.icons['file-psd.svg'], type: 'icon'};
}
// *.py
else if(fsentry.name.toLowerCase().endsWith('.py')){
return {image: window.icons['file-py.svg'], type: 'icon'};
}
// *.xlsx
else if(fsentry.name.toLowerCase().endsWith('.xlsx')){
return {image: window.icons['file-xlsx.svg'], type: 'icon'};
}
// --------------------------------------------------
// Determine icon by set or derived mime type
// --------------------------------------------------
else if(fsentry.type){
return {image: content_type_to_icon(fsentry.type), type: 'icon'};
}
else{
return {image: content_type_to_icon(mime.getType(fsentry.name)), type: 'icon'};
}
}
/**
* Asynchronously checks if a save account notice should be shown to the user, and if needed, displays the notice.
*
@ -1605,7 +1423,7 @@ window.move_items = async function(el_items, dest_path, is_undo = false){
associated_app_name: fsentry.associated_app?.name,
uid: fsentry.uid,
path: fsentry.path,
icon: await window.item_icon(fsentry),
icon: await item_icon(fsentry),
name: (dest_path === window.trash_path) ? $(el_item).attr('data-name') : fsentry.name,
is_dir: fsentry.is_dir,
size: fsentry.size,
@ -1634,7 +1452,7 @@ window.move_items = async function(el_items, dest_path, is_undo = false){
immutable: false,
uid: dir.uid,
path: dir.path,
icon: await window.item_icon(dir),
icon: await item_icon(dir),
name: dir.name,
size: dir.size,
type: dir.type,
@ -2277,7 +2095,7 @@ window.rename_file = async(options, new_name, old_name, old_path, el_item, el_it
$(el_item_name_editor).hide();
// Set new icon
const new_icon = (options.is_dir ? window.icons['folder.svg'] : (await window.item_icon(fsentry)).image);
const new_icon = (options.is_dir ? window.icons['folder.svg'] : (await item_icon(fsentry)).image);
$(el_item_icon).find('.item-icon-icon').attr('src', new_icon);
// Set new `data-name`

204
src/helpers/item_icon.js Normal file
View File

@ -0,0 +1,204 @@
/**
* 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 mime from "../lib/mime.js";
import content_type_to_icon from './content_type_to_icon.js';
/**
* Assigns an icon to a filesystem entry based on its properties such as name, type,
* and whether it's a directory, app, trashed, or specific file type.
*
* @function item_icon
* @global
* @async
* @param {Object} fsentry - A filesystem entry object. It may contain various properties
* like name, type, path, associated_app, thumbnail, is_dir, and metadata, depending on
* the type of filesystem entry.
*/
const item_icon = async (fsentry)=>{
// --------------------------------------------------
// If this file is Trashed then set the name to the original name of the file before it was trashed
// --------------------------------------------------
if(fsentry.path?.startsWith(window.trash_path + '/')){
if(fsentry.metadata){
try{
let metadata = JSON.parse(fsentry.metadata);
fsentry.name = (metadata && metadata.original_name) ? metadata.original_name : fsentry.name
}
catch(e){
// Ignored
}
}
}
// --------------------------------------------------
// thumbnail
// --------------------------------------------------
if(fsentry.thumbnail){
return {image: fsentry.thumbnail, type: 'thumb'};
}
// --------------------------------------------------
// app icon
// --------------------------------------------------
else if(fsentry.associated_app && fsentry.associated_app?.name){
if(fsentry.associated_app.icon)
return {image: fsentry.associated_app.icon, type: 'icon'};
else
return {image: window.icons['app.svg'], type: 'icon'};
}
// --------------------------------------------------
// Trash
// --------------------------------------------------
else if(fsentry.shortcut_to_path && fsentry.shortcut_to_path === window.trash_path){
// get trash image, this is needed to get the correct empty vs full trash icon
let trash_img = $(`.item[data-path="${html_encode(window.trash_path)}" i] .item-icon-icon`).attr('src')
// if trash_img is undefined that's probably because trash wasn't added anywhere, do a direct lookup to see if trash is empty or no
if(!trash_img){
let trashstat = await puter.fs.stat(window.trash_path);
if(trashstat.is_empty !== undefined && trashstat.is_empty === true)
trash_img = window.icons['trash.svg'];
else
trash_img = window.icons['trash-full.svg'];
}
return {image: trash_img, type: 'icon'};
}
// --------------------------------------------------
// Directories
// --------------------------------------------------
else if(fsentry.is_dir){
// System Directories
if(fsentry.path === window.docs_path)
return {image: window.icons['folder-documents.svg'], type: 'icon'};
else if (fsentry.path === window.pictures_path)
return { image: window.icons['folder-pictures.svg'], type: 'icon' };
else if (fsentry.path === window.home_path)
return { image: window.icons['folder-home.svg'], type: 'icon' };
else if (fsentry.path === window.videos_path)
return { image: window.icons['folder-videos.svg'], type: 'icon' };
else if (fsentry.path === window.desktop_path)
return { image: window.icons['folder-desktop.svg'], type: 'icon' };
else if (fsentry.path === window.public_path)
return { image: window.icons['folder-public.svg'], type: 'icon' };
// regular directories
else
return {image: window.icons['folder.svg'], type: 'icon'};
}
// --------------------------------------------------
// Match icon by file extension
// --------------------------------------------------
// *.doc
else if(fsentry.name.toLowerCase().endsWith('.doc')){
return {image: window.icons['file-doc.svg'], type: 'icon'};
}
// *.docx
else if(fsentry.name.toLowerCase().endsWith('.docx')){
return {image: window.icons['file-docx.svg'], type: 'icon'};
}
// *.exe
else if(fsentry.name.toLowerCase().endsWith('.exe')){
return {image: window.icons['file-exe.svg'], type: 'icon'};
}
// *.gz
else if(fsentry.name.toLowerCase().endsWith('.gz')){
return {image: window.icons['file-gzip.svg'], type: 'icon'};
}
// *.jar
else if(fsentry.name.toLowerCase().endsWith('.jar')){
return {image: window.icons['file-jar.svg'], type: 'icon'};
}
// *.java
else if(fsentry.name.toLowerCase().endsWith('.java')){
return {image: window.icons['file-java.svg'], type: 'icon'};
}
// *.jsp
else if(fsentry.name.toLowerCase().endsWith('.jsp')){
return {image: window.icons['file-jsp.svg'], type: 'icon'};
}
// *.log
else if(fsentry.name.toLowerCase().endsWith('.log')){
return {image: window.icons['file-log.svg'], type: 'icon'};
}
// *.mp3
else if(fsentry.name.toLowerCase().endsWith('.mp3')){
return {image: window.icons['file-mp3.svg'], type: 'icon'};
}
// *.rb
else if(fsentry.name.toLowerCase().endsWith('.rb')){
return {image: window.icons['file-ruby.svg'], type: 'icon'};
}
// *.rss
else if(fsentry.name.toLowerCase().endsWith('.rss')){
return {image: window.icons['file-rss.svg'], type: 'icon'};
}
// *.rtf
else if(fsentry.name.toLowerCase().endsWith('.rtf')){
return {image: window.icons['file-rtf.svg'], type: 'icon'};
}
// *.sketch
else if(fsentry.name.toLowerCase().endsWith('.sketch')){
return {image: window.icons['file-sketch.svg'], type: 'icon'};
}
// *.sql
else if(fsentry.name.toLowerCase().endsWith('.sql')){
return {image: window.icons['file-sql.svg'], type: 'icon'};
}
// *.tif
else if(fsentry.name.toLowerCase().endsWith('.tif')){
return {image: window.icons['file-tif.svg'], type: 'icon'};
}
// *.tiff
else if(fsentry.name.toLowerCase().endsWith('.tiff')){
return {image: window.icons['file-tiff.svg'], type: 'icon'};
}
// *.wav
else if(fsentry.name.toLowerCase().endsWith('.wav')){
return {image: window.icons['file-wav.svg'], type: 'icon'};
}
// *.cpp
else if(fsentry.name.toLowerCase().endsWith('.cpp')){
return {image: window.icons['file-cpp.svg'], type: 'icon'};
}
// *.pptx
else if(fsentry.name.toLowerCase().endsWith('.pptx')){
return {image: window.icons['file-pptx.svg'], type: 'icon'};
}
// *.psd
else if(fsentry.name.toLowerCase().endsWith('.psd')){
return {image: window.icons['file-psd.svg'], type: 'icon'};
}
// *.py
else if(fsentry.name.toLowerCase().endsWith('.py')){
return {image: window.icons['file-py.svg'], type: 'icon'};
}
// *.xlsx
else if(fsentry.name.toLowerCase().endsWith('.xlsx')){
return {image: window.icons['file-xlsx.svg'], type: 'icon'};
}
// --------------------------------------------------
// Determine icon by set or derived mime type
// --------------------------------------------------
else if(fsentry.type){
return {image: content_type_to_icon(fsentry.type), type: 'icon'};
}
else{
return {image: content_type_to_icon(mime.getType(fsentry.name)), type: 'icon'};
}
}
export default item_icon;

View File

@ -16,11 +16,13 @@
* 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 '../UI/UIWindow.js';
import UIAlert from '../UI/UIAlert.js';
import i18n from '../i18n/i18n.js';
import launch_app from "./launch_app.js";
import path from '../lib/path.js';
import item_icon from './item_icon.js';
const open_item = async function(options){
let el_item = options.item;
@ -154,7 +156,7 @@ const open_item = async function(options){
UIWindow({
path: item_path,
title: path.basename(item_path),
icon: await window.item_icon({is_dir: true, path: item_path}),
icon: await item_icon({is_dir: true, path: item_path}),
uid: $(el_item).attr('data-uid'),
is_dir: is_dir,
app: 'explorer',

View File

@ -19,6 +19,7 @@
import path from '../lib/path.js';
import UIItem from '../UI/UIItem.js';
import item_icon from './item_icon.js';
const refresh_item_container = function(el_item_container, options){
options = options || {};
@ -177,7 +178,7 @@ const refresh_item_container = function(el_item_container, options){
immutable: fsentry.immutable,
associated_app_name: fsentry.associated_app?.name,
path: item_path,
icon: await window.item_icon(fsentry),
icon: await item_icon(fsentry),
name: (metadata && metadata.original_name !== undefined) ? metadata.original_name : fsentry.name,
is_dir: fsentry.is_dir,
multiselectable: !is_openFileDialog,

View File

@ -44,7 +44,7 @@ import { SettingsService } from './services/SettingsService.js';
import UIComponentWindow from './UI/UIComponentWindow.js';
import update_mouse_position from './helpers/update_mouse_position.js';
import { LaunchOnInitService } from './services/LaunchOnInitService.js';
import item_icon from './helpers/item_icon.js';
const launch_services = async function (options) {
// === Services Data Structures ===
@ -204,7 +204,7 @@ window.initgui = async function(options){
UIWindow({
path: item_path,
title: path.basename(item_path),
icon: await window.item_icon({is_dir: true, path: item_path}),
icon: await item_icon({is_dir: true, path: item_path}),
is_dir: true,
app: 'explorer',
});