Skip to content

Commit

Permalink
Finish allowing macOS app to use original filesystem path to open dro…
Browse files Browse the repository at this point in the history
…pped files.

Setup the client-side file drag-n-drop code to check if the file-paths have been saved, and if so, then use the same path that Electron based app could use for dropped files.
Fixed error uploading foreground+background files at same time when using the filesystem path.
Fixed native apps not showing error message when user tried uploading multiple files at a time, and foreground+background was not obvious.
Converted calls to the `miscSignal` to all use JS to find the proper element ID to use in the emit signal (rather than having to pass it from c++).
  • Loading branch information
wcjohns committed Jan 20, 2024
1 parent ab2b2ba commit 34f2697
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 59 deletions.
82 changes: 55 additions & 27 deletions js/InterSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ function()

if( !validin )
{
alert( 'You can only upload a single file at a time unless the name is'
+ ' prefixed with "i-", "b-", "k-" or has back/fore/item/calib'
+ ' in the file name, and there is at most one of each spectrum type.' );
const msg = 'showMsg-error-You can only upload a single file at a time unless the name is'
+ ' prefixed with "i-", "b-", "k-" or has back/fore/item/calib'
+ ' in the file name, and there is at most one of each spectrum type.';

Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, msg );
return;
}

Expand Down Expand Up @@ -102,36 +104,62 @@ function()
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
// Filename may have non-ISO-8859-1 code points, so we need to URI encode it
xhr.setRequestHeader("X-File-Name", encodeURIComponent(file.name));
const filename_uri = encodeURIComponent(file.name);
xhr.setRequestHeader("X-File-Name", filename_uri);


//#if( BUILD_AS_ELECTRON_APP ) //C++ preprocessors dont look to work here...
if( lookForPath && (typeof file.path === "string") && (file.path.length > 2) && !file.path.toLowerCase().includes('fake') ) {
// For Electron builds, file.path gives the full path (including filename), so we
// will just upload the path so we can read it in the c++ to avoid lots of copying and
// stuff. But we also need to fallback, JIC c++ fails for some unforeseen reason.
// See #FileDragUploadResource::handleRequest for the other part of this logic
xhr.setRequestHeader("Is-File-Path", "1" );
if( lookForPath && file.name && (file.name.length > 3) ){
let fspath = null;
if( (typeof file.path === "string") && file.path.length > 3 ){
fspath = file.path;
}else{
const fns = $(document).data('dragOverFilePaths');
if( fns && Array.isArray(fns.filenames) && fns.time && (Math.abs(fns.time - (new Date())) < 30000) ){
for( let i = 0; i < fns.filenames.length; ++i ) {
if( encodeURIComponent(fns.filenames[i]).endsWith(filename_uri) ){
fspath = fns.filenames[i];
//remove this filename from the array, incase there are multiple files with same leaf-name, but diff paths
fns.filenames = fns.filenames.splice(i,i);
console.log( "Matched filenames: ", file.name, " vs ", fspath );
console.log( "Remaining files: ", fns.filenames );
if( fns.filenames.length === 0 )
$(document).data('dragOverFilePaths', null);
break;
}//if( native fn ends with browsers fn )
}//for( loop over fns.filenames.length )
}//if( we have 'dragOverFilePaths' object, and its valid )
}//if( we have `file.path` available ) else if( we have 'dragOverFilePaths' avaiable )

if( (typeof fspath === "string") && (fspath.length > 2) && !fspath.toLowerCase().includes('fake') ) {
// For Electron and macOS builds, fspath gives the full path (including filename), so we
// will just upload the path so we can read it in the c++ to avoid lots of copying and
// stuff. But we also need to fallback, JIC c++ fails for some unforeseen reason.
// See #FileDragUploadResource::handleRequest for the other part of this logic
xhr.setRequestHeader("Is-File-Path", "1" );

console.log( 'Will send file path, instead of actual file; path: ', file.path );
console.log( 'Will send native file-system path, instead of actual file; path: ', fspath );

xhr.onload = function(){
removeUploading();
xhr.onload = function(){
removeUploading();

if( (xhr.readyState === 4) && (xhr.status !== 200) ){
console.log( 'Failed to upload via Electron path.' );
uploadFileToUrl( uploadURL, file, false );
}else if( xhr.readyState === 4 ) {
console.log( 'Successfully uploaded path to Electron.' );
}
};
if( (xhr.readyState === 4) && (xhr.status !== 200) ){
// Fallback to standard http upload, since reading in c++ failed
console.log( 'Failed to upload via native file-system path.' );
uploadFileToUrl( uploadURL, file, false );
}else if( xhr.readyState === 4 ) {
// Reading in c++ succeeded
console.log( 'Successfully uploaded native file-system path.' );
uploadNextFile();
}
};

xhr.onloadend = removeUploading;
xhr.onloadend = removeUploading;

const body = JSON.stringify( { fullpath: file.path} );
xhr.send(body);
return;
}//if( have full path on Electron build )
//#endif
const body = JSON.stringify( { fullpath: fspath} );
xhr.send(body);
return;
}//if( have full path on Electron or macOS build )
}

xhr.onerror = errfcn;
xhr.onload = function(){
Expand Down
2 changes: 1 addition & 1 deletion src/FileDragUploadResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ void FileDragUploadResource::handleRequest( const Http::Request& request,
}//if( !app )


#if( BUILD_AS_ELECTRON_APP )
#if( BUILD_AS_ELECTRON_APP || BUILD_AS_OSX_APP )
// See FileUploadFcn in InterSpec.js
const string isFilePath = request.headerValue("Is-File-Path");

Expand Down
2 changes: 1 addition & 1 deletion src/FluxTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1183,7 +1183,7 @@ void FluxToolWidget::init()
m_copyBtn = new WPushButton( "Copy To Clipboard" );

// TODO: "upgrade" to using the InterSpecApp 'miscSignal' directly in CopyFluxDataTextToClipboard, and get rid of m_infoCopied signal handler
//"Wt.emit( '" + wApp->dom()->id() + "', {name:'miscSignal'}, 'showMsg-info-' );"
//"Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, 'showMsg-info-' );"

m_copyBtn->clicked().connect( "function(s,e){ "
"var success = Wt.WT.CopyFluxDataTextToClipboard(s,e,'" + m_copyBtn->id() + "'); "
Expand Down
8 changes: 4 additions & 4 deletions src/InterSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7374,7 +7374,7 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent )
true, HelpSystem::ToolTipPosition::Right );

const string msg = "Changes will not take effect until app is restarted or refreshed."
"<div onclick=\"Wt.emit('" + wApp->root()->id() + "',{name:'miscSignal'}, 'clearSession');"
"<div onclick=\"Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, 'clearSession');"
"try{$(this.parentElement.parentElement).remove();}catch(e){}return false;\" "
"class=\"clearsession\"><span class=\"clearsessiontxt\">Refresh Session</span></div>";
const WString wmsg = WString::fromUTF8( msg );
Expand Down Expand Up @@ -10677,7 +10677,7 @@ void InterSpec::setSpectrum( std::shared_ptr<SpecMeas> meas,
WStringStream js;
js << "File contains GPS coordinates."
<< "<div onclick="
"\"Wt.emit('" << wApp->root()->id() << "',{name:'miscSignal'}, 'showMap-" << type << "');"
"\"Wt.emit( $('.specviewer').attr('id'),{name:'miscSignal'}, 'showMap-" << type << "');"
"try{$(this.parentElement.parentElement).remove();}catch(e){}"
"return false;\" "
"class=\"clearsession\">"
Expand Down Expand Up @@ -10807,7 +10807,7 @@ void InterSpec::setSpectrum( std::shared_ptr<SpecMeas> meas,
#endif
<< riidAnaSummary(meas)
<< "<div onclick="
"\"Wt.emit('" << wApp->root()->id() << "',{name:'miscSignal'}, 'showRiidAna-" << type << "');"
"\"Wt.emit( $('.specviewer').attr('id'),{name:'miscSignal'}, 'showRiidAna-" << type << "');"
//"$('.qtip.jgrowl:visible:last').remove();"
"try{$(this.parentElement.parentElement).remove();}catch(e){}"
"return false;\" "
Expand Down Expand Up @@ -10928,7 +10928,7 @@ void InterSpec::setSpectrum( std::shared_ptr<SpecMeas> meas,
WStringStream js;
js << "Spectrum file contained embedded images: "
<< "<div onclick="
"\"Wt.emit('" << wApp->root()->id() << "',{name:'miscSignal'}, 'showMultimedia-" << type << "');"
"\"Wt.emit( $('.specviewer').attr('id'),{name:'miscSignal'}, 'showMultimedia-" << type << "');"
//"$('.qtip.jgrowl:visible:last').remove();"
"try{$(this.parentElement.parentElement).remove();}catch(e){}"
"return false;\" "
Expand Down
4 changes: 2 additions & 2 deletions src/InterSpecApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ void InterSpecApp::setupWidgets( const bool attemptStateLoad )
WStringStream js;
js << "Resuming where you left off on " << state->name.toUTF8()
<< "<div onclick="
"\"Wt.emit('" << root()->id() << "',{name:'miscSignal'}, 'clearSession');"
"\"Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, 'clearSession');"
//"$('.qtip.jgrowl:visible:last').remove();"
"try{$(this.parentElement.parentElement).remove();}catch(e){}"
"return false;\" "
Expand Down Expand Up @@ -1502,7 +1502,7 @@ void InterSpecApp::miscSignalHandler( const std::string &signal )
level = WarningWidget::WarningMsgLevel::WarningMsgInfo;
}else if( SpecUtils::istarts_with( msg, "error-" ) )
{
msg = signal.substr(6);
msg = msg.substr(6);
level = WarningWidget::WarningMsgLevel::WarningMsgHigh;
}

Expand Down
12 changes: 6 additions & 6 deletions src/QrCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ namespace
{
WT_DECLARE_WT_MEMBER
(CopyUrlToClipboard, Wt::JavaScriptFunction, "CopyUrlToClipboard",
function( sender, event, id, domSignalId, text )
function( sender, event, id, text )
{
const success_msg = 'showMsg-info-Copied URL to clipboard';
const fail_msg = 'showMsg-error-Failed to copy URL to clipboard';
Expand Down Expand Up @@ -87,22 +87,22 @@ WT_DECLARE_WT_MEMBER
if( successful )
{
console.log('Copied text to clipboard via appending textarea to DOM');
Wt.emit( domSignalId, {name:'miscSignal'}, success_msg );
Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, success_msg );
}else
{
console.warn('Failed to copy to clipboard using textarea');
Wt.emit( domSignalId, {name:'miscSignal'}, fail_msg );
Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, fail_msg );
}

return successful;
}//if( !navigator.clipboard )

navigator.clipboard.writeText(text).then(function() {
console.log('Copying text to clipboard using async method.');
Wt.emit( domSignalId, {name:'miscSignal'}, success_msg );
Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, success_msg );
}, function(err) {
console.warn('Failed copying text to clipboard using async method.: ', err);
Wt.emit( domSignalId, {name:'miscSignal'}, fail_msg );
Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, fail_msg );
});
}
);
Expand Down Expand Up @@ -467,7 +467,7 @@ SimpleDialog *displayTxtAsQrCode( const std::string &url,
// TODO: there is probably a way to get wApp->root()->id() directly in the JS
const string escaped_url = WString(url).jsStringLiteral();
copyBtn->clicked().connect( "function(s,e){ "
"Wt.WT.CopyUrlToClipboard(s,e,'" + copyBtn->id() + "','" + wApp->root()->id() + "'," + escaped_url + ");"
"Wt.WT.CopyUrlToClipboard(s,e,'" + copyBtn->id() + "'," + escaped_url + ");"
"}" );
if( is_phone )
{
Expand Down
8 changes: 4 additions & 4 deletions src/RemoteRid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1115,13 +1115,13 @@ class ExternalRidWidget : public Wt::WContainerWidget
nucstr += (i ? "," : "") + nuclides[i];

js <<
"<div onclick=\"Wt.emit('" << app->root()->id() << "',{name:'miscSignal'}, 'showRemoteRidRefLines-" << nucstr << "');"
"<div onclick=\"Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, 'showRemoteRidRefLines-" << nucstr << "');"
"try{$(this.parentElement.parentElement.parentElement).remove();}catch(e){}"
"return false;\">Show Ref. Lines</div>";
}//if( !nuclides.empty() )

js <<
"<div onclick=\"Wt.emit('" << app->root()->id() << "',{name:'miscSignal'}, 'openRemoteRidTool');"
"<div onclick=\"Wt.emit( $('.specviewer').attr('id'), {name:'miscSignal'}, 'openRemoteRidTool');"
"try{$(this.parentElement.parentElement.parentElement).remove();}catch(e){}"
"return false;\">Open Remote RID</div>";

Expand All @@ -1147,10 +1147,10 @@ class ExternalRidWidget : public Wt::WContainerWidget
js << msg
<<
"<div class=\"RemoteRidToastButtons\">"
"<div onclick=\"Wt.emit('" << app->root()->id() << "',{name:'miscSignal'}, 'openRemoteRidTool');"
"<div onclick=\"Wt.emit( $('.specviewer').attr('id'),{name:'miscSignal'}, 'openRemoteRidTool');"
"try{$(this.parentElement.parentElement.parentElement).remove();}catch(e){}"
"return false;\">Open Remote RID</div>"
"<div onclick=\"Wt.emit('" << app->root()->id() << "',{name:'miscSignal'}, 'disableRemoteRid');"
"<div onclick=\"Wt.emit( $('.specviewer').attr('id'),{name:'miscSignal'}, 'disableRemoteRid');"
"try{$(this.parentElement.parentElement.parentElement).remove();}catch(e){}"
"return false;\">Disable Remote RID</div>"
"</div>";
Expand Down
17 changes: 3 additions & 14 deletions target/osx/AppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender

// We wont handle promises here, so this way WkWebView will keep handling them,
// But here is some code to intercept them, in case it turns out we should.
// (TODO: double check Mail and Outlook attachments can be dragged in)
//NSPasteboard *pboard = [sender draggingPasteboard];
//NSDictionary *options = @{};
//NSArray<Class> *promises_class = @[[NSFilePromiseReceiver class]];
Expand Down Expand Up @@ -619,20 +620,8 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
if( !doResume )
actualURL = [NSString stringWithFormat:@"%@&restore=no", actualURL];


/*
//Right now drag-n-drop from Outlook does not work - should investigate this:
// Probably need to sub-class WKWebView, and then do something like
//https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/DragandDrop/Tasks/acceptingdrags.html#//apple_ref/doc/uid/20000993-BABHHIHC
*/





//[_window registerForDraggedTypes: [NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]]; //requires macOS >=10.13, which is the lowest we target anyway
[_window registerForDraggedTypes: [NSArray arrayWithObjects:NSPasteboardTypeFileURL, NSPasteboardTypeURL, NSFileContentsPboardType, nil]]; //requires macOS >=10.13, which is the lowest we target anyway

// WkWebView registers _window for registerForDraggedTypes; if it didnt, we could use the following line
//[_window registerForDraggedTypes: [NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]];

//if( [_InterSpecWebView respondsToSelector:@selector(mainFrame)])
//[[_InterSpecWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:actualURL]]];
Expand Down

0 comments on commit 34f2697

Please sign in to comment.