Comprehensive UI and State Management Overhaul in JS Files: Implemented gdeState for centralized state handling, introduced GDELogin class for structured login functionality, enhanced dropdowns with clear and onchange handlers, and updated button setups to align with cloud storage. Included event-driven login/logout mechanism and dynamic UI updates in response to user authentication in ui_modifications.js. In ui_helpers.js, refined button and modal setups for improved interaction, and optimized dropdown management for user-centric UI updates.

This commit is contained in:
Tasha Upchurch
2023-11-28 20:11:19 -05:00
parent 15b670d9b3
commit b23aa748e4
2 changed files with 415 additions and 210 deletions
+81 -48
View File
@@ -1,15 +1,16 @@
import {app} from "../../../scripts/app.js";
import {api} from "../../../scripts/api.js";
export function setupButtons(menu, app, api) {
export function setupButtons(gdeState, app, api) {
// Save To Git Button
const menu = document.querySelector(".comfy-menu");
const saveButton = document.createElement("button");
saveButton.textContent = "Save To Git";
saveButton.textContent = "Save To Cloud";
saveButton.onclick = async () => {
try {
const json = JSON.stringify(app.graph.serialize(), null, 2);
const userNameValue = menu.querySelector("#branch-name-field").value;
const dropDownSelected = menu.querySelector("#graph-name-field").value;
const userNameValue = gdeState.user;
const graphSelected = gdeState.graph;
await api.fetchApi("/gde/git/save", {
method: "POST",
@@ -18,7 +19,7 @@ export function setupButtons(menu, app, api) {
},
body: JSON.stringify({
data: json,
file_name: dropDownSelected,
file_name: graphSelected,
user_name: userNameValue
}),
});
@@ -30,9 +31,11 @@ export function setupButtons(menu, app, api) {
// Load From Git Button
const loadButton = document.createElement("button");
loadButton.textContent = "Load From Git";
loadButton.textContent = "Load From Cloud";
loadButton.onclick = async () => {
try {
const userNameValue = gdeState.user;
const graphSelected = gdeState.graph;
const response = await api.fetchApi("/gde/git/load", {
// needs user_name file_name
method: "POST",
@@ -40,8 +43,8 @@ export function setupButtons(menu, app, api) {
"Content-Type": "application/json",
},
body: JSON.stringify({
user_name: menu.querySelector("#branch-name-field").value,
file_name: menu.querySelector("#graph-name-field").value
user_name: userNameValue,
file_name: graphSelected
}
),
@@ -56,9 +59,10 @@ export function setupButtons(menu, app, api) {
}
export function setupModal() {
export function setupModal(gdeState) {
// Modal
const modal = document.createElement("div");
modal.id = "graph-name-modal";
modal.style.display = "none"; // Initially hidden
modal.style.position = "fixed";
modal.style.top = "50%";
@@ -80,7 +84,10 @@ export function setupModal() {
const submitButton = document.createElement("button");
submitButton.textContent = "Submit";
let targetDropdownId = ''; // This will store the ID of the dropdown we're targeting
submitButton.onclick = () => {
modal.submit = () => {
// this is the function that will be called when the submit button is clicked
// it will add the new option to the dropdown and select it
const dropdown = document.querySelector(`#${targetDropdownId}`);
const newOption = document.createElement("option");
newOption.value = modalInput.value;
@@ -89,6 +96,10 @@ export function setupModal() {
dropdown.value = modalInput.value;
console.log("Submitted value:", modalInput.value);
modal.style.display = "none"; // Hide the modal
return modalInput.value;
}
submitButton.onclick = () => {
modal.submit()
};
modal.append(submitButton);
@@ -99,13 +110,37 @@ export function setupModal() {
};
}
HTMLSelectElement.prototype.clearDropDown = function (placeHolder = "") {
// remove everything
while (this.options.length > 0) {
this.remove(0);
}
// only add if there is a placeholder
if (placeHolder != "") {
//add select+type
const defaultOption = document.createElement("option");
defaultOption.value = "";
defaultOption.textContent = placeHolder
this.append(defaultOption);
export function setupDropdown(menu, api, placeholder, apiEndpoint, dropdownId, user = null, graph_name = null, onCreateNew = null) {
// Add "Create New" option
const createNewOption = document.createElement("option");
createNewOption.value = "create_new";
createNewOption.textContent = "Create New";
this.append(createNewOption);
}
};
export function setupDropdown(menu, api, placeholder, apiEndpoint, dropdownId,
user = null, onCreateNew = null,
onChanged = null) {
let dropdown = document.getElementById(dropdownId);
// If dropdown already exists, clear its options first
if (dropdown) {
dropdown.innerHTML = '';
dropdown.clearDropDown(placeholder)
} else {
// Create new dropdown if it doesn't exist
dropdown = document.createElement("select");
@@ -118,55 +153,49 @@ export function setupDropdown(menu, api, placeholder, apiEndpoint, dropdownId, u
dropdown.id = dropdownId;
//use comfy-list style
dropdown.clearDropDown(placeholder)
menu.append(dropdown);
// Add event listener to the dropdown
dropdown.addEventListener("change", (event) => {
// Check if "Create New" option was selected
if (event.target.value === "create_new" && onCreateNew) {
onCreateNew(event, dropdown);
}
if (onChanged) {
onChanged(event, dropdown);
}
}
);
}
// Add default option
const defaultOption = document.createElement("option");
defaultOption.value = "";
defaultOption.textContent = placeholder;
dropdown.append(defaultOption);
// Add "Create New" option
const createNewOption = document.createElement("option");
createNewOption.value = "create_new";
createNewOption.textContent = "Create New";
dropdown.append(createNewOption);
dropdown.addEventListener("change", (event) => {
// Check if "Create New" option was selected
if (event.target.value === "create_new" && onCreateNew) {
onCreateNew(event, dropdown);
}
// Check if branch dropdown is the one being changed
if (dropdownId === 'branch-name-field') {
while (dropdown.options.length > 2) {
dropdown.remove(2);
}
setTimeout(async () => {
await someFunction(dropdownId, api, apiEndpoint, placeholder, user, graph_name)
})
}
});
// dropdown.addEventListener("change", (event) => {
// // Check if "Create New" option was selected
// if (event.target.value === "create_new" && onCreateNew) {
// onCreateNew(event, dropdown);
// }
//
// });
setTimeout(async () => {
await someFunction(dropdownId, api, apiEndpoint, placeholder, user, graph_name)
})
dropdown.clearDropDown(placeholder)
await someFunction(dropdownId, api, apiEndpoint, placeholder, user)
}, 50)
return dropdown;
}
export async function someFunction(dropdownId, api, apiEndpoint, placeholder, user = null, graph_name = null) {
export async function someFunction(dropdownId, api, apiEndpoint, placeholder, user = null) {
// this is the function that will be called to fetch the options for the dropdown
try {
const dropdown = document.getElementById(dropdownId);
dropdown.disabled = true; // Disable the file dropdown
const clientId = api.socket.url.split("=")[1]
const queryParams = {
user: user || ""
user: user || "",
client_id: clientId
};
if (graph_name) {
queryParams.graph_name = graph_name;
}
const response = await api.fetchApi(apiEndpoint, {
method: "POST",
@@ -177,6 +206,10 @@ export async function someFunction(dropdownId, api, apiEndpoint, placeholder, us
});
const optionsData = await response.json();
optionsData.forEach(optionItem => {
// if the option already exists, don't add it again
if (dropdown.querySelector(`option[value="${optionItem.value}"]`)) {
return;
}
const optionElement = document.createElement("option");
optionElement.value = optionItem.value;
optionElement.textContent = optionItem.label;
+334 -162
View File
@@ -2,153 +2,233 @@ import {app} from "../../../scripts/app.js";
import {api} from "../../../scripts/api.js";
import {setupButtons, setupModal, setupDropdown} from './ui_helpers.js';
let loggedInEvent = null;
let loggedOutEvent = new Event('loggedOutEvent');
export let selectedGraph = null;
//const graphCreateNewEvent = new Event('graphCreateNew');
// create a place to store some variables, including security_token
const gde = {
security_token: null,
}
gde.login_div = document.createElement("div");
gde.login_div.id = "gde-login";
gde.usernameField = document.createElement("input");
gde.usernameField.id = "username-field";
gde.usernameField.placeholder = "Username";
//add a logout button
gde.logoutButton = document.createElement("button");
gde.logoutButton.id = "logout-button";
gde.logoutButton.innerText = "Logout";
gde.logoutButton.addEventListener("click", async () => {
//clear the token
gde.security_token = null;
//remove the logout button
gde.logoutButton.remove();
//add the login button and fields
gde.login_div.append(gde.usernameField);
gde.login_div.append(gde.passwordField);
gde.login_div.append(gde.loginButton);
// clear the graph dropdown
const graphDropdown = document.getElementById("graph-name-field");
if (graphDropdown) {
// remove all options without removing the first two
while (graphDropdown.options.length > 2) {
graphDropdown.remove(2);
}
}
// clear the branch dropdown
const branchDropdown = document.getElementById("branch-name-field");
if (branchDropdown) {
// remove all options without removing the first two
while (branchDropdown.options.length > 2) {
branchDropdown.remove(2);
}
}
// tell the server we logged out
await api.fetchApi("/gde/logout", {
method: "POST",
body: JSON.stringify({
client_id: api.clientId,
})
}
);
class GDELogin {
constructor(api) {
this.api = api;
this.security_token = null;
this.initElements();
}
);
gde.passwordField = document.createElement("input");
gde.passwordField.id = "password-field";
gde.passwordField.placeholder = "Password";
gde.passwordField.type = "password";
gde.loginButton = document.createElement("button");
gde.loginButton.id = "login-button";
gde.loginButton.style.color = "var(--input-text)";
gde.loginButton.style.backgroundColor = "var(--comfy-input-bg)";
gde.loginButton.style.borderRadius = "8px";
gde.loginButton.style.borderColor = "var(--border-color)";
gde.loginButton.style.borderStyle = "solid";
gde.loginButton.style.marginTop = "2px";
gde.loginButton.style.width = "100%";
gde.loginButton.innerText = "Login";
gde.loginButton.addEventListener("click", async () => {
const username = document.getElementById("username-field").value;
const password = document.getElementById("password-field").value;
const res = await api.fetchApi("/gde/login", {
method: "POST",
body: JSON.stringify({
username: username,
password: password,
client_id: api.clientId,
}
)
}
)
;
initElements() {
this.login_div = this.createElement("div", {id: "gde-login"});
this.usernameField = this.createInputField("username-field", "Username");
this.passwordField = this.createInputField("password-field", "Password", "password");
this.loginButton = this.createButton("login-button", "Login", this.handleLogin.bind(this));
this.logoutButton = this.createButton("logout-button", "Logout", this.handleLogout.bind(this));
}
createInputField(id, placeholder, type = "text") {
return this.createElement("input", {
id: id,
placeholder: placeholder,
type: type,
});
}
createButton(id, text, clickHandler) {
const button = this.createElement("button", {
id: id,
innerText: text,
});
button.addEventListener("click", clickHandler);
return button;
}
createElement(tag, attributes) {
const element = document.createElement(tag);
for (let attr in attributes) {
element[attr] = attributes[attr];
}
return element;
}
async handleLogin() {
const username = this.usernameField.value;
const password = this.passwordField.value;
gdeState.user = username;
const res = await this.api.fetchApi("/gde/login", {
method: "POST",
body: JSON.stringify({
username: username,
password: password,
client_id: this.api.clientId,
}),
});
if (res.status !== 200) {
alert("Login failed");
//if res.logged_in is false then the login failed but only need to alert if this.api.clientId is set
if (res.logged_in === false && this.api.clientId) {
alert("Login failed. Please check your username and password and try again.");
}
if (res.logged_in === true) {
this.username = res.user;
gdeState.user = username;
}
} else {
//read the json response, token will be under security
const json = await res.json();
const security_json = await res.json();
this.security_token = security_json.security;
this.switchToLogoutView();
//set the token it is somewhere in the json under security
gde.security_token = json.security;
if (username) {
const event = new CustomEvent('loggedInEvent',
{detail: {'user': username}});
//loggedInEvent.target = {value:json.user};
document.dispatchEvent(event);
} else { // client was refreshed user was lost but client_id was not
this.usernameField.value = security_json.user;
loggedInEvent.target.value = security_json.user;
document.dispatchEvent(loggedInEvent);
//now that we are logged in remove the login button and fields
gde.usernameField.remove();
gde.passwordField.remove();
gde.loginButton.remove();
}
const welcomeMessage = this.createElement("div", {
id: "welcome-message",
innerText: `Welcome ${this.usernameField.value}`
});
this.login_div.append(welcomeMessage);
}
gde.login_div.append(gde.logoutButton);
}
async handleLogout() {
await this.api.fetchApi("/gde/logout", {
method: "POST",
body: JSON.stringify({
client_id: this.api.clientId,
}),
});
document.dispatchEvent(loggedOutEvent);
this.clearDropdowns();
this.switchToLoginView();
const welcomeMessage = document.getElementById("welcome-message");
if (welcomeMessage) welcomeMessage.remove();
}
switchToLogoutView() {
this.usernameField.remove();
this.passwordField.remove();
this.loginButton.remove();
this.login_div.append(this.logoutButton);
}
switchToLoginView() {
this.logoutButton.remove();
this.login_div.append(this.usernameField, this.passwordField, this.loginButton);
}
clearDropdowns() {
this.clearDropdown("graph-name-field");
//this.clearDropdown("branch-name-field");
}
clearDropdown(id) {
const dropdown = document.getElementById(id);
if (dropdown) {
while (dropdown.options.length > 2) {
dropdown.remove(2);
}
}
}
);
gde.get_server_state = async (client_id) => {
// check with the server to see if we have a valid client_id
const response = await api.fetchApi("/gde/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
client_id: client_id,
}),
});
const data = await response.json();
return data;
async getServerState(client_id) {
// check with the server to see if we have a valid client_id
const response = await api.fetchApi("/gde/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
client_id: client_id,
}),
});
const data = await response.json();
return data;
}
async setupLoginForm() {
const menu = document.querySelector(".comfy-menu");
// create a div in the menu for this extension
//need to check with server for the status of our current api.clientId
const cid = api.clientId || api.socket.url.split("=")[1];
const server_state = await this.getServerState(cid).then((server_state) => {
const logged_in = server_state.logged_in;
gdeState.user = server_state.user;
const separator = document.createElement("hr");
separator.style.margin = "20px 0";
separator.style.width = "100%";
menu.append(separator);
menu.append(this.login_div);
if (logged_in) {
this.login_div.append(this.logoutButton);
} else {
this.login_div.append(this.usernameField);
this.login_div.append(this.passwordField);
this.login_div.append(this.loginButton);
}
});
}
}
const gdeLogin = new GDELogin(api);
let gdeState = {
// this is for holding the state of the ui like what is selected etc
graph: null,
branch: null,
user: null,
}
// create a dummy function
gde.setup_login_form = async () => {
const menu = document.querySelector(".comfy-menu");
// create a div in the menu for this extension
//need to check with server for the status of our current api.clientId
const cid = api.clientId || api.socket.url.split("=")[1];
const server_state = await gde.get_server_state(cid).then((server_state) => {
const logged_in = server_state.logged_in;
const separator = document.createElement("hr");
separator.style.margin = "20px 0";
separator.style.width = "100%";
menu.append(separator);
menu.append(gde.login_div);
if (logged_in) {
gde.login_div.append(gde.logoutButton);
} else {
gde.login_div.append(gde.usernameField);
gde.login_div.append(gde.passwordField);
gde.login_div.append(gde.loginButton);
}
});
}
gde.hijack_queueprompt = async () => {
// is this actually needed?
}
// gde.setup_login_form = async () => {
// const menu = document.querySelector(".comfy-menu");
// // create a div in the menu for this extension
//
//
// //need to check with server for the status of our current api.clientId
// const cid = api.clientId || api.socket.url.split("=")[1];
// const server_state = await gde.get_server_state(cid).then((server_state) => {
// const logged_in = server_state.logged_in;
//
// const separator = document.createElement("hr");
// separator.style.margin = "20px 0";
// separator.style.width = "100%";
// menu.append(separator);
// menu.append(gde.login_div);
//
// if (logged_in) {
// gde.login_div.append(gde.logoutButton);
//
// } else {
// gde.login_div.append(gde.usernameField);
// gde.login_div.append(gde.passwordField);
// gde.login_div.append(gde.loginButton);
//
// }
// });
//
// }
// gde.hijack_queueprompt = async () => {
// // is this actually needed?
// }
app.registerExtension({
name: "GDE.UI.RemoveDefaultButtons",
async setup(app) {
@@ -161,60 +241,152 @@ app.registerExtension({
name: "GDE.UI.Login",
async setup() {
setTimeout(async () => {
await gde.setup_login_form();
await gde.hijack_queueprompt();
await gdeLogin.setupLoginForm();
//await gde.hijack_queueprompt();
}, 10);
}
});
function SetupGraphDropDown(graphDropdown,
event,
menu,
handleGraphCreateNew,
handleGraphSelect) {
// event may come in as null, so we need to populate it with default values
if (!event) {
event = {
detail: {
user: "main",
graph: "default.json",
branch: "main"
},
target: {
value: "main"
}
}
gdeState.user = "main"
}
// graphDropdown.clearDropDown();
graphDropdown.clearDropDown("Select Graph");
const selectedGraph = null
graphDropdown = setupDropdown(menu, api,
"Select Graph",
"/gde/git/user_graphs",
"graph-name-field",
gdeState.user,
handleGraphCreateNew,
handleGraphSelect);
// modify graphDropdown to change gdeState.graph when it is changed
graphDropdown.addEventListener("change", (event) => {
const selectedGraph = event.target.value;
if (selectedGraph && selectedGraph !== gdeState.graph) {
gdeState.graph = selectedGraph;
const graphChangeEvent = new CustomEvent('graphChangeEvent', {
detail: {
graph: selectedGraph
}
});
document.dispatchEvent(graphChangeEvent);
}
});
return graphDropdown;
}
app.registerExtension({
name: "GDE.UI.Git",
async setup() {
setupModal();
setupButtons(gdeState, app, api);
const separator = document.createElement("hr");
const menu = document.querySelector(".comfy-menu");
separator.style.margin = "20px 0";
separator.style.width = "100%";
menu.append(separator);
setupButtons(menu, app, api);
const handleBranchCreateNew = (event, dropdown) => {
// Custom logic for when "Create New" is selected in the branch dropdown
showModal('branch-name-field'); // Show the modal for creating a new branch
dropdown.value = ""; // Reset the dropdown value
// Clear the file dropdown
const fileDropdown = document.getElementById("graph-name-field");
if (fileDropdown) {
// remove all options without removing the first two
while (fileDropdown.options.length > 2) {
fileDropdown.remove(2);
}
}
};
const handleGraphCreateNew = (event, dropdown) => {
// Custom logic for when "Create New" is selected in the branch dropdown
showModal('graph-name-field'); // Show the modal for creating a new branch
dropdown.value = ""; // Reset the dropdown value
// Clear the file dropdown
const fileDropdown = document.getElementById("graph-name-field");
};
setupDropdown(menu, api, "Select Branch", "/gde/git/users", "branch-name-field", null, null, handleBranchCreateNew);
const branchDropdown = document.getElementById("branch-name-field");
// const handleBranchCreateNew = (event, dropdown) => {
// // Custom logic for when "Create New" is selected in the branch dropdown
// showModal('branch-name-field'); // Show the modal for creating a new branch
// dropdown.value = ""; // Reset the dropdown value
// // Clear the file dropdown
// const fileDropdown = document.getElementById("graph-name-field");
//
//
// };
branchDropdown.addEventListener("change", (event) => {
const selectedBranch = event.target.value;
if (selectedBranch && selectedBranch !== "Create New") {
// Repopulate the file/graph dropdown based on the selected branch
setupDropdown(menu, api, "Select Graph", "/gde/git/user_graphs", "graph-name-field", selectedBranch, null, handleGraphCreateNew);
}
document.addEventListener('loggedInEvent', async (event) => {
gdeState.user = event.detail.user;
//gdeState.graph = event.detail.graph;
graphDropdown = SetupGraphDropDown(
graphDropdown,
event,
menu,
handleGraphCreateNew,
handleGraphSelect);
});
// no lets call clear dropdown when there is a loggout
document.addEventListener('loggedOutEvent', async (event) => {
graphDropdown.clearDropDown();
}
);
setupDropdown(menu, api, "Select Graph", "/gde/git/user_graphs", "graph-name-field", null, null, handleGraphCreateNew);
const handleGraphCreateNew = (event, dropdown) => {
// Custom logic for when "Create New" is selected in the branch dropdown
showModal('graph-name-field')
dropdown.disabled = true;
const modal = document.getElementById("graph-name-modal");
// hijack its onsubmit function
const prev_submit = modal.submit;
//submitButton.onclick
modal.submit = () => {
let graphName = prev_submit();
gdeState.graph = graphName;
dropdown.disabled = false;
}
};
const handleGraphSelect = (event, dropdown) => {
// change gdeState.graph when it is changed
const selectedGraph = event.target.value;
gdeState.graph = selectedGraph;
};
//setupDropdown(menu, api, "Select Branch", "/gde/git/users", "branch-name-field", null, null, handleBranchCreateNew);
//const branchDropdown = document.getElementById("branch-name-field");
// branchDropdown.addEventListener("change", (event) => {
// const selectedBranch = event.target.value;
// if (selectedBranch && selectedBranch !== "Create New") {
// // Repopulate the file/graph dropdown based on the selected branch
// setupDropdown(menu, api, "Select Graph", "/gde/git/user_graphs", "graph-name-field", selectedBranch, null, handleGraphCreateNew);
// }
//
// });
let tmp = setupDropdown(menu,
api,
"Select Graph",
"/gde/git/user_graphs",
"graph-name-field",
gdeState.user,
handleGraphCreateNew,
handleGraphSelect);
let graphDropdown = SetupGraphDropDown(tmp,
null,
menu,
handleGraphCreateNew,
handleGraphSelect);
}
});