Skip to content

Commit

Permalink
Merge pull request #1043 from OSC/files_app_clean_history_final
Browse files Browse the repository at this point in the history
Files app clean history final
  • Loading branch information
ericfranz authored Apr 15, 2021
2 parents 5b3f44a + e84f688 commit e7aba2a
Show file tree
Hide file tree
Showing 381 changed files with 3,249 additions and 3 deletions.
4 changes: 3 additions & 1 deletion apps/dashboard/app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
//
//= require data-confirm-modal
//= require handlebars.js/4.4.2/handlebars.min
//= require lodash/4.17.15/lodash.min.js
//= require lodash/4.17.15/lodash.min
//= require uppy/dist/uppy.min
//= require_tree .
//= stub editor

//FIXME: move to coffescript
$(function(){
Expand Down
218 changes: 218 additions & 0 deletions apps/dashboard/app/assets/javascripts/editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@

$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});

var KEY_PREFIX = "ood_editor_store_";

// Set localStorage. Adds a key prefix to reduce overlap likelihood.
function setLocalStorage(key, value) {
var ood_key = KEY_PREFIX + key;
localStorage.setItem(ood_key, value);
return null;
}

// Get localStorage. Adds a key prefix added by setter.
function getLocalStorage(key) {
var ood_key = KEY_PREFIX + key;
return localStorage.getItem(ood_key);
}

// Set a user preference key to a specific value.
function setUserPreference(key, value) {
return setLocalStorage(key, value);
}

// Get the current value of the key from preferences.
function getUserPreference(key) {
return getLocalStorage(key);
}


$( document ).ready(function () {

// Do not load the ace editor if the element is not available
// ex. for directory views
if ( $( '#editor' ).length ) {
$( '#error' ).hide();
// Initialize the ace editor
var editor = ace.edit("editor");
setOptions();
editor.setReadOnly(true);
$( "#loading-notice" ).toggle();
var loading = true;
// Load the file via ajax
var loadedContent = $.ajax({
url: apiUrl,
type: 'GET',
dataType: "text",
success: function (data) {
editorContent = data;
$("#editor").text(editorContent);
editor.destroy();
editor = ace.edit("editor");
initializeEditor();
setModeFromModelist();
$( "#loading-notice" ).toggle();
setBeforeUnloadState();
loading = false;
},
error: function (request, status, error) {
$( '#error' ).show();
editor.destroy();
$( '#editor' ).remove();
$( '#ajaxErrorResponse' ).text(error);
$( "#loading-notice" ).toggle();
}
});
function initializeEditor() {

// Disables/enables the save button and binds the window popup if there are changes
editor.on("change", function() {
// The dirtyCounter is an undocumented array in the UndoManager
// Changing the editor only modifies the dirtyCounter after the event is over,
// so we set it manually on change to apply the proper unload state
// https://github.com/ajaxorg/ace/blob/4a55188fdb0eee9e2d3854f175e67408a1e47655/lib/ace/undomanager.js#L164
editor.session.getUndoManager().dirtyCounter++;
setBeforeUnloadState();
});

// Mark the editor as clean after load.
editor.session.getUndoManager().markClean();

// Disable the save button after the initial load
// Modifying settings and adding data to the editor makes the UndoManager "dirty"
// so we have to explicitly re-disable it on page ready.
$( "#save-button" ).prop("disabled", true);

// Set the caret at inside the editor on load.
editor.focus();
};

function setSaveButtonState() {
$( "#save-button" ).prop("disabled", editor.session.getUndoManager().isClean());
};

function setBeforeUnloadState() {
if ( loading ) {
editor.session.getUndoManager().markClean();
};

setSaveButtonState();

window.onbeforeunload = function (e) {
if (!editor.session.getUndoManager().isClean()) {
return 'You have unsaved changes!';
} else {
// return nothing
};
};
};

// Toggles a spinner in place of the save icon
function toggleSaveSpinner() {
$( "#save-icon" ).toggleClass("glyphicon-save");
$( "#save-icon" ).toggleClass("glyphicon-refresh");
$( "#save-icon" ).toggleClass("glyphicon-spin");
};

// Toggles a checkbox in place of the save icon
function toggleSaveConfirmed() {
$( "#save-icon" ).toggleClass("glyphicon-save");
$( "#save-icon" ).toggleClass("glyphicon-saved");
};

// Sets the key binding to the selected option
function setKeyBinding() {
var binding = $( "#keybindings option:selected" ).val();
if (binding == "default") {
binding = null;
}
editor.setKeyboardHandler( binding );
};

// Change the font size
$( "#fontsize" ).change(function() {
editor.setFontSize( $( "#fontsize option:selected" ).val() );
setUserPreference( 'fontsize', $( "#fontsize option:selected" ).val() );
});

// Change the key bindings
$( "#keybindings" ).change(function() {
setKeyBinding();
setUserPreference( 'keybindings', $( "#keybindings option:selected" ).val() );
});

// Change the theme
$( "#theme" ).change(function() {
editor.setTheme( $( "#theme option:selected" ).val() );
setUserPreference( 'theme', $( "#theme option:selected" ).val() );
});

// Change the mode
$( "#mode" ).change(function() {
editor.getSession().setMode( "ace/mode/" + $( "#mode option:selected" ).val() );
setUserPreference( 'mode', $( "#mode option:selected" ).val() );
});

// Change the word wrap setting
$( "#wordwrap" ).change(function() {
editor.getSession().setUseWrapMode(this.checked);
setUserPreference( 'wordwrap', $( "#wordwrap" ).is(':checked') );
});

// Save button onclick handler
// sends the content to the cloudcmd api via PUT request
$( "#save-button" ).click(function() {
if (apiUrl !== "") {
$( "#save-button" ).prop("disabled", true);
toggleSaveSpinner();
$.ajax({
url: apiUrl,
type: 'PUT',
data: editor.getValue(),
contentType: 'text/plain',
success: function (data) {
toggleSaveSpinner();
toggleSaveConfirmed();
setTimeout(function () {
toggleSaveConfirmed();
}, 2000);

editor.session.getUndoManager().markClean();
$( "#save-button" ).prop("disabled", editor.session.getUndoManager().isClean());
setBeforeUnloadState();
},
error: function (request, status, error) {
alert("An error occured attempting to save this file!\n" + error);
toggleSaveSpinner();
}
});
} else {
console.log("Can't save this!");
};
});

// Automatically Sets the dropdown and mode to the modelist option
function setModeFromModelist() {
var modelist = ace.require("ace/ext/modelist").getModeForPath(filePath);
$( "#mode" ).val(modelist.name);
editor.session.setMode(modelist.mode);
};

function setOptions() {
$( "#keybindings" ).val(getUserPreference('keybindings') || "default");
setKeyBinding();
$( "#fontsize" ).val(getUserPreference('fontsize') || '12px');
editor.setFontSize( $( "#fontsize option:selected" ).val() );
$( "#mode" ).val(getUserPreference('mode') || "text");
editor.session.setMode( "ace/mode/" + $( "#mode option:selected" ).val() );
$( "#theme" ).val(getUserPreference('theme') || "ace/theme/solarized_light");
editor.setTheme( $( "#theme option:selected" ).val() );
$( "#wordwrap" ).prop("checked", getUserPreference('wordwrap') === "true");
editor.getSession().setUseWrapMode( $( "#wordwrap" ).is(':checked'));
};

initializeEditor();
}
});
89 changes: 89 additions & 0 deletions apps/dashboard/app/assets/javascripts/files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
function alertError(error_title, error_message){
Swal.fire(error_title, error_message, 'error');
}

function dataFromJsonResponse(response){
return new Promise((resolve, reject) => {
Promise.resolve(response)
.then(response => response.ok ? Promise.resolve(response) : Promise.reject(new Error(response.statusText)))
.then(response => response.json())
.then(data => data.error_message ? Promise.reject(new Error(data.error_message)) : resolve(data))
.catch((e) => reject(e))
});
}

function newFile(filename){
fetch(`${history.state.currentDirectoryUrl}/${encodeURI(filename)}?touch=true`, {method: 'put', headers: { 'X-CSRF-Token': csrf_token }})
.then(response => dataFromJsonResponse(response))
.then(() => reloadTable())
.catch(e => alertError('Error occurred when attempting to create new file', e.message));
}

function newDirectory(filename){
fetch(`${history.state.currentDirectoryUrl}/${encodeURI(filename)}?dir=true`, {method: 'put', headers: { 'X-CSRF-Token': csrf_token }})
.then(response => dataFromJsonResponse(response))
.then(() => reloadTable())
.catch(e => alertError('Error occurred when attempting to create new directory', e.message));
}

function reloadTable(url){
var request_url = url || history.state.currentDirectoryUrl;

return fetch(request_url, {headers: {'Accept':'application/json'}})
.then(response => dataFromJsonResponse(response))
.then(function(data){
table.clear();
table.rows.add(data.files);
table.draw();

$('#open-in-terminal-btn').attr('href', data.shell_url);
$('#open-in-terminal-btn').removeClass('disabled');

return Promise.resolve(data);
})
.catch((e) => {
Swal.fire(e.message, `Error occurred when attempting to access ${request_url}`, 'error');

$('#open-in-terminal-btn').addClass('disabled');
return Promise.reject(e);
});
}

function goto(url, pushState = true, show_processing_indicator = true) {
if(url == history.state.currentDirectoryUrl)
pushState = false;

reloadTable(url)
.then((data) => {
$('#path-breadcrumbs').html(data.breadcrumbs_html);

if(pushState) {
// Clear search query when moving to another directory.
table.search('').draw();

history.pushState({
currentDirectory: data.path,
currentDirectoryUrl: data.url
}, data.name, data.url);
}
})
.finally(() => {
//TODO: after processing is available via ActiveJobs merge
// if(show_processing_indicator)
// table.processing(false)
});
}

function loading(title){
Swal.fire({
title: title,
allowOutsideClick: false,
showConfirmButton: false,
willOpen: () => { Swal.showLoading() }
});
}

function doneLoading(){
Swal.close();
}

4 changes: 3 additions & 1 deletion apps/dashboard/app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,6 @@ pre.motd-monospaced {
@import "fa_shims";
@import "fontawesome-iconpicker";
@import "breadcrumb";
@import "buttons";
@import "buttons";
@import "files";
@import "uppy/dist/uppy.min";
181 changes: 181 additions & 0 deletions apps/dashboard/app/assets/stylesheets/editor.scss

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions apps/dashboard/app/assets/stylesheets/files.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#directory-contents .selected {
color: white;
background-color: rgb(0, 136, 204);
}

#directory-contents .selected a.name {
color: white;
font-weight: bold;
}

#directory-contents.dragover {
border: 5px solid rgb(51, 122, 183) !important;
}

//FIXME: jquery datatables lets us modify layout using "dom" but that doesn't let you specify anything
// other than div elements; so we will merge these into one that can fit in a single div when we use a react component
// that can manage state of both so when one is updated the other remains untouched
//
// the react component can make use of
div.datatables-status, div.transfers-status {
display: inline;
}
Loading

0 comments on commit e7aba2a

Please sign in to comment.