Improved public folder file links (#1687)

* Don't update URL when opening public files via direct URL

* Alert user when attempting to open a nonexistent file

* Fix toolbar autohide in public file urls

* Remove extraneous debug logs

* Properly define stat var in public folder URL handling

* Update window URL when focusing an app opened through file URL

* Localize error messages
This commit is contained in:
Hariz S. authored and GitHub committed 2025-10-27 15:35:11 -07:00
1 parent fd8a88b8f4
commit 2487251bb9
5 files changed
+140 -129

No files matched your search

+109 -98
View File
@@ -1391,104 +1391,6 @@ async function UIDesktop(options) {
})
}
//--------------------------------------------------------------------------------------
// Trying to view a user's public folder?
// i.e. https://puter.com/@<username>
//--------------------------------------------------------------------------------------
const url_paths = window.location.pathname.split('/').filter(element => element);
if (url_paths[0]?.startsWith('@')) {
const username = url_paths[0].substring(1);
let item_path = '/' + username + '/Public';
if ( url_paths.length > 1 ) {
item_path += '/' + url_paths.slice(1).join('/');
}
// GUARD: avoid invalid user directories
{
if (!username.match(/^[a-z0-9_]+$/i)) {
UIAlert({
message: 'Invalid username.'
});
return;
}
}
const stat = await puter.fs.stat({path: item_path, consistency: 'eventual'});
// TODO: DRY everything here with open_item. Unfortunately we can't
// use open_item here because it's coupled with UI logic;
// it requires a UIItem element and cannot operate on a
// file path on its own.
if ( ! stat.is_dir ) {
if ( stat.associated_app ) {
launch_app({ name: stat.associated_app.name });
return;
}
const ext_pref =
window.user_preferences[`default_apps${path.extname(item_path).toLowerCase()}`];
if ( ext_pref ) {
launch_app({
name: ext_pref,
file_path: item_path,
});
return;
}
const open_item_meta = await $.ajax({
url: window.api_origin + "/open_item",
type: 'POST',
contentType: "application/json",
data: JSON.stringify({
path: item_path,
}),
headers: {
"Authorization": "Bearer "+window.auth_token
},
statusCode: {
401: function () {
window.logout();
},
},
});
const suggested_apps = open_item_meta?.suggested_apps ?? await window.suggest_apps_for_fsentry({
path: item_path
});
// Note: I'm not adding unzipping logic here. We'll wait until
// we've refactored open_item so that Puter can have a
// properly-reusable open function.
if ( suggested_apps.length !== 0 ) {
launch_app({
name: suggested_apps[0].name,
token: open_item_meta.token,
file_path: item_path,
app_obj: suggested_apps[0],
window_title: path.basename(item_path),
maximized: options.maximized,
file_signature: open_item_meta.signature,
});
return;
}
await UIAlert({
message: 'Cannot find an app to open this file; ' +
'opening directory instead.'
});
item_path = item_path.split('/').slice(0, -1).join('/')
}
UIWindow({
path: item_path,
title: path.basename(item_path),
icon: await item_icon({ is_dir: true, path: item_path }),
is_dir: true,
app: 'explorer',
});
}
window.hide_toolbar = (animate = true) => {
// Always show toolbar on mobile and tablet devices
if (isMobile.phone || isMobile.tablet) {
@@ -1770,6 +1672,115 @@ async function UIDesktop(options) {
}
}
});
//--------------------------------------------------------------------------------------
// Trying to view a user's public folder?
// i.e. https://puter.com/@<username>
//--------------------------------------------------------------------------------------
const url_paths = window.location.pathname.split('/').filter(element => element);
if (url_paths[0]?.startsWith('@')) {
const username = url_paths[0].substring(1);
let item_path = '/' + username + '/Public';
if ( url_paths.length > 1 ) {
item_path += '/' + url_paths.slice(1).join('/');
}
// GUARD: avoid invalid user directories
{
if (!username.match(/^[a-z0-9_]+$/i)) {
UIAlert({
message: i18n('error_invalid_username')
});
return;
}
}
let stat;
try {
stat = await puter.fs.stat({path: item_path, consistency: 'eventual'});
} catch ( e ) {
window.history.replaceState(null, document.title, '/');
UIAlert({
message: i18n('error_user_or_path_not_found'),
type: 'error'
});
return;
}
// TODO: DRY everything here with open_item. Unfortunately we can't
// use open_item here because it's coupled with UI logic;
// it requires a UIItem element and cannot operate on a
// file path on its own.
if ( ! stat.is_dir ) {
if ( stat.associated_app ) {
launch_app({ name: stat.associated_app.name });
return;
}
const ext_pref =
window.user_preferences[`default_apps${path.extname(item_path).toLowerCase()}`];
if ( ext_pref ) {
launch_app({
name: ext_pref,
file_path: item_path,
});
return;
}
const open_item_meta = await $.ajax({
url: window.api_origin + "/open_item",
type: 'POST',
contentType: "application/json",
data: JSON.stringify({
path: item_path,
}),
headers: {
"Authorization": "Bearer "+window.auth_token
},
statusCode: {
401: function () {
window.logout();
},
},
});
const suggested_apps = open_item_meta?.suggested_apps ?? await window.suggest_apps_for_fsentry({
path: item_path
});
// Note: I'm not adding unzipping logic here. We'll wait until
// we've refactored open_item so that Puter can have a
// properly-reusable open function.
if ( suggested_apps.length !== 0 ) {
launch_app({
name: suggested_apps[0].name,
token: open_item_meta.token,
file_path: item_path,
app_obj: suggested_apps[0],
window_title: path.basename(item_path),
maximized: options.maximized,
file_signature: open_item_meta.signature,
custom_path: window.location.pathname,
});
return;
}
await UIAlert({
message: 'Cannot find an app to open this file; ' +
'opening directory instead.'
});
item_path = item_path.split('/').slice(0, -1).join('/')
}
UIWindow({
path: item_path,
title: path.basename(item_path),
icon: await item_icon({ is_dir: true, path: item_path }),
is_dir: true,
app: 'explorer',
});
}
}
$(document).on('contextmenu taphold', '.taskbar', function (event) {
+16 -1
View File
@@ -121,6 +121,9 @@ async function UIWindow(options) {
options.is_visible = options.is_visible ?? true;
// used for files opened via direct url
options.custom_path = options.custom_path ?? null;
// if only one instance is allowed, bring focus to the window that is already open
if(options.single_instance && options.app !== ''){
let $already_open_window = $(`.window[data-app="${html_encode(options.app)}"]`);
@@ -252,6 +255,7 @@ async function UIWindow(options) {
data-sort_order ="${options.sort_order ?? 'asc'}"
data-multiselectable = "${options.selectable_body}"
data-update_window_url = "${options.update_window_url && options.is_visible}"
data-custom_path = "${html_encode(options.custom_path)}"
data-user_set_url_params = "${html_encode(user_set_url_params)}"
data-initial_zindex = "${zindex}"
data-is_panel ="${options.is_panel ? 1 : 0}"
@@ -3745,7 +3749,18 @@ $.fn.focusWindow = function(event) {
//change window URL
const update_window_url = $(this).attr('data-update_window_url');
const url_app_name = $(this).attr('data-app_pseudonym') || $(this).attr('data-app');
if(update_window_url === 'true' || update_window_url === null){
let custom_path = $(this).attr('data-custom_path');
if (custom_path && custom_path !== ''){
if(update_window_url === 'true' || update_window_url === null){
if (!custom_path.startsWith('/')) {
custom_path = '/' + custom_path;
}
window.history.replaceState({window_id: $(this).attr('data-id')}, '', custom_path);
document.title = $(this).attr('data-name');
}
}
else if(update_window_url === 'true' || update_window_url === null){
window.history.replaceState({window_id: $(this).attr('data-id')}, '', '/app/'+url_app_name+$(this).attr('data-user_set_url_params'));
document.title = $(this).attr('data-name');
}
+11
View File
@@ -368,6 +368,15 @@ const launch_app = async (options)=>{
if(window_options?.has_head !== undefined)
has_head = window_options.has_head;
// update_window_url
let update_window_url = true;
if (options.update_window_url !== undefined && typeof options.update_window_url === 'boolean')
update_window_url = options.update_window_url;
let custom_path;
if(options.custom_path !== undefined)
custom_path = options.custom_path;
// open window
el_win = UIWindow({
element_uuid: uuid,
@@ -392,6 +401,8 @@ const launch_app = async (options)=>{
is_resizable: window_resizable,
has_head: has_head,
show_in_taskbar: show_in_taskbar,
update_window_url: update_window_url,
custom_path: custom_path,
});
// If the app is not in the background, show the window
+3
View File
@@ -509,6 +509,9 @@ const en = {
'open_containing_folder': 'Open Containing Folder',
'set_as_background': 'Set as Desktop Background',
'error_user_or_path_not_found': 'User or path not found.',
'error_invalid_username': 'Invalid username.',
}
};